前言: 本文是学习<<go语言程序设计>> -- 清华大学出版社(王鹏 编著) 的2014年1月第一版 做的一些笔记 , 如有侵权, 请告知笔者, 将在24小时内删除, 转载请注明出处!

1. Goroutine

  - 定义: 在语言级别上支持的轻量级线程.

  - Go标准库提供的所有操作系统调用操作(包括同步I/O操作), 都会让出处理机给它. 所以它的切换和管理不依赖于系统的进程和线程

  - 是go语言库的功能 , 而不是操作系统的功能. Goroutine不是用线程实现的. 本质是一段代码, 一个函数入口, 以及在堆上为其分配的一个堆栈.

  - 省去了频繁创建和销毁线程的开销, 可以创建上百万个Goroutine, 但是它们并不是直接被操作系统调度.

  - 其他语言一般通过库的方式支持协程, 但是在这种协程中调用一个同步IO操作,比如网络通信, 本地文件读写都会阻塞其他并发执行的协程.

  - 创建方式: 在一个函数前面加上 go 关键字, 这次调用就会在一个新的Goroutine中并发执行, 直到函数返回.( 注: 函数返回值会被丢弃)

2. Channel

  - 定义 : Go 采用消息机制作为并发单位之间的通信手段.

  - 每个并发单位是自包含的, 独立的个体, 并且都有自己的变量. 这些变量不能在不同的并发单位之间共享.

  - 类似Pipe, 可以使用Channel在两个或多个Goroutine之间传递消息,  Channel在设计上确保同一时刻只有一个Goroutine能从中接收数据. 从而避免使用互斥锁的问题.

  - Channel的发送和接收都是原语操作, 不会中断, 只会失败.

  - Channel是进程内的通信方式, 进程间通信一般采用分布式的方法, 如: Socket 或者HTTP等.

  - 声明和初始化:   var channelName chan ElementType  ElementType 指定Channel所能传递的元素类型.

3. 数据的接收和发送

  - 使用Channel在不同的Goroutine中传递数据. 使用通道运算符号:  <-

  - 发送  channelVar <- value

  - 接收  value := <- ch

  - 向Channel写入数据会使  程序(应该是Goroutine吧)阻塞, 知道有其他的Goroutine从这个Channel中读取数据.

  - 如果Channel之前没有写入数据, 那么从Channel中读取数据也会阻塞  程序(应该是Goroutine吧), 直到有写入数据为止.

package main

import(
"fmt"
"math/rand"
) func Test(ch chan int){
// 不明白结果中为什么有的End没有输出来?
fmt.Println("Begin...",ch)
ch <- rand.Int()
fmt.Println("End ...",ch)
} func main() {
chs := make([]chan int,10)
for i := 0;i<10;i++ {
chs[i] = make(chan int)
go Test(chs[i])
}
for _,ch := range chs{
value := <- ch
fmt.Println(value)
}
}

4. Channel的关闭和迭代器

  - 关闭: 使用GO的内置函数 close() :  close(chName)

  - 关闭一个Channel只后往往还有判断Channel是否被关闭:  value,ok := <- ch // ok 为false表示ch已经关闭.

  - 使用迭代器读取.

    - 使用range

package main

import (
"fmt"
) func main() {
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}() // 这里立即调用
for value := range ch {
fmt.Println(value)
}
}

    - 使用  if value, ok := <- ch; ok { ... } // 判断是否已经关闭ch, 从而遍历ch的值

package main

import (
"fmt"
) func main() {
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}()
for {
if value, ok := <-ch; ok {
fmt.Println(value)
} else {
break
}
}
}

  - 如果接收端使用range或者循环接受数据, 那么必须调用close()

5. 单向Channel

  - 定义:

    - 只写(只能接收):  var chWrite chan <- ElementType

    - 只读(只能发送):  var chRead <- chan ElementType

  - 定义了单向Channel之后要对其初始化, 由于Channel是引用数据类型, 也是一个原生数据类型, 因此不仅支持被传递, 还支持类型转换(由双向转单向).

ch := make(chan,int)
chRead := <- chan int(ch)
chWrite := chan <- int(ch)

  - 实例

package main

import (
"fmt"
) func Recv(ch <-chan int, lock chan<- bool) {
for value := range ch {
fmt.Println(value)
}
lock <- true
} func Send(ch chan<- int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
} func main() {
ch := make(chan int)
lock := make(chan bool)
go Recv(ch, lock) // 传参的时候转换为单向Channel
go Send(ch)
<-lock // 这是关闭操作吗?
}

6. 异步Channel

  - 对于传递少量数据的应用来说一般使用同步Channel, 对于传递大量数据而言需要使用异步Channel.

  - 使用Buffer 来实现消息队列. 设定一个Buffer的大小 , 在Buffer未写满时不阻塞发送操作, 在Buffer未读完之前, 不阻塞接受操作.

  - 创建:  ch := make(chan int, 1024) // 指定缓冲区大小即创建了一个带Buffer的Channel.

  - 代码

package main

import (
"fmt"
"time"
) func Worker(sem chan int, lock chan bool, id int) {
sem <- 1 // 相当于对同步信号量进行P原语操作.
fmt.Println(time.Now().Format("04:05"), id)
time.Sleep(1 * time.Second)
<-sem // 相当于对同步信号量进行V原语操作
if id == 9 {
lock <- true
}
} func main() {
ch := make(chan int, 2)
lock := make(chan bool)
for i := 0; i < 10; i++ {
go Worker(ch, lock, i)
}
<-lock
}

7. Select机制和超时机制

  - Select用于解决通道通信中多路复用的问题. 因为通道的接收操作往往是阻塞式的, 所以Select经常和超时机制配合.

  - 如果有多个channel需要监听, 就可以使用Select随机选择一个可用的channel进行处理.

  - Select在接收端进行监听, 当然Select也可以用于发送端, 如果有其他Goroutine在运行, 还可以使用空Select{}阻塞main()函数, 避免进程终止.

  - 代码:

package main

import (
"fmt"
) func main() {
ch1 := make(chan int)
ch2 := make(chan string)
lock := make(chan bool)
go func() {
for {
select {
case value, ok := <-ch1:
if !ok {
lock <- true
break
}
fmt.Println("ch1 = ", value)
case value, ok := <-ch2:
if !ok {
lock <- true
break
}
fmt.Println("ch2 = ", value)
}
}
}()
ch1 <- 100
ch2 <- "golang"
ch2 <- "Go"
ch1 <- 200
close(ch1)
close(ch2)
<-lock
}

  - Select机制

    - 当所有被监听Channel中都没有数据时, Select会等到其中一个有数据为止.

    - 当多个被监听Channel中都有数据时, Select会随机选择一个case执行.

    - 当所有被监听Channel中都没有数据时, 如果default子句存在, 则将会被执行.

    - 如果想持续监听多个Channel, 需要使用for语句协助(how?)

  -  超时机制

    - Go中并发通信过程中所有的错误处理都是由超时机制来完成的

    - 超时机制是一种解决通信死锁的机制, 通常会设置一个超时参数, 通信双方如果在设定的时间内仍然没有处理完任务, 则处理过程会被立即终止, 并返回相应的超时信息.

    - Go语言没有直接提供超时处理机制, 可以用Select机制解决

package main

import (
"fmt"
"time"
) func main() {
ch := make(chan int)
timeout := make(chan bool, 1)
go func() {
time.Sleep(1 * time.Second)
timeout <- true
}()
select {
case <-ch:
case <-timeout:
fmt.Println("Timeout")
break
}
}

    - Go 语言的time包中还提供了After,Tick 等函数, 可以返回计时器Channel, 利用它们也可以实现超时机制.

Go 语言并发笔记的更多相关文章

  1. Go语言并发与并行学习笔记(三)

    转:http://blog.csdn.net/kjfcpua/article/details/18265475 Go语言并发的设计模式和应用场景 以下设计模式和应用场景来自Google IO上的关于G ...

  2. MIT 6.824学习笔记3 Go语言并发解析

    之前看过一个go语言并发的介绍:https://www.cnblogs.com/pdev/p/10936485.html   但这个太简略啦.下面看点深入的 还记得https://www.cnblog ...

  3. Java:并发笔记-01

    Java:并发笔记-01 说明:这是看了 bilibili 上 黑马程序员 的课程 java并发编程 后做的笔记 1. 进程与线程 本章内容 进程和线程的概念 并行和并发的概念 线程基本应用 1.1 ...

  4. HTML语言学习笔记(会更新)

    # HTML语言学习笔记(会更新) 一个html文件是由一系列的元素和标签组成的. 标签: 1.<html></html> 表示该文件为超文本标记语言(HTML)编写的.成对出 ...

  5. Go语言并发编程示例 分享(含有源代码)

    GO语言并发示例分享: ppt http://files.cnblogs.com/files/yuhan-TB/GO%E8%AF%AD%E8%A8%80.pptx 代码, 实际就是<<Go ...

  6. JavaScript语言精粹笔记

    JavaScript语言精粹笔记 掌握语言的每个特性可以让你出风头,但是并不推荐,因为一部分的特性带来的麻烦可能远超本身的价值.正如书中所言,坏的材料并不能雕刻出好的作品,要成为一名更好的程序员,要取 ...

  7. c语言的笔记

    下面把我这半年来记的一些C语言的笔记贴出来. 1  C语言中函数参数传递是按照“值传递”进行的,即单向传递. 2  函数原型:函数类型 函数名(参数类型,参数类型……),可以不必加参数名,因为操作系统 ...

  8. C语言盲点笔记1

    寥寥数笔,记录我的C语言盲点笔记,仅仅为以前经历过,亦有误,可交流. 1.int* a和int *a有差别吗? 没有不论什么差别,都表示a是int指针 建议这么写int *a;这样明显一点 理由例如以 ...

  9. C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com

    原文:C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | I ...

随机推荐

  1. jmeter踩坑系列

    1.踩坑系列一: 抓包出来有host的字段,放到jmeter里面一起请求就报错了,去掉就请求正常了 1.踩坑系列二: 从花瓶复制过去 的values 前面有空格,肉眼看起来没有

  2. laravel 环境自编译过程

    [原创] 看到此文的朋友看完后也许会失望,但我尽最大努力不让搜友们失望,以下是自己操作的笔记用以整理提高 虽然 laravel 官方已给出了安装 laravel 框架所需的环境盒子 使用Vagrant ...

  3. 《Java核心技术》 -- 读书笔记 ② - 类 | 对象 | 接口

    对象vs对象变量 “对象” 描述的是一个类的具体实例,他被java虚拟机分配在 "堆" (Heap)中. “对象变量” 为一个对象的引用(对象变量的值=记载着具体对象的位置/地址) ...

  4. 在使用Ajax请求返回json数据的时候IE浏览器弹出下载保存对话框的解决方法

    在使用Ajax请求返回json数据的时候IE浏览器弹出下载保存对话框的解决方法 最近在做一个小东西,使用kindeditor上传图片的时候,自己写了一个上传的方法,按照协议规则通过ajax返回json ...

  5. 匿名方法,lambad表达式,匿名类

    其实lambad表达式就是“函数”或者说是“方法”写法的一个进化,越来越简化而已,如数学方法里的f(X). 匿名方法:顾名思义,匿名方法就是没有名称的方法,但是有定义参数. 匿名方法最明显的好处就是可 ...

  6. Linux 之不同运维人员共用root 账户权限审计

    一.为什么? 在中小型企业,公司不同运维人员基本都是以root 账户进行服务器的登陆管理,缺少了账户权限审计制度.不出问题还好,出了问题,就很难找出源头. 这里介绍下,如何利用编译bash 使不同的客 ...

  7. BFC以及margin的深入探究

    BFC(Block Formatting Context) 块级格式化上下文,它是指一个独立的块级渲染区域,只有block-level Box参与,该区域拥有一套渲染规则来约束块级盒子的布局,且与区域 ...

  8. Django的路由层(URLconf)

    URL配置(URLconf)就像Django所支撑网站的目录.它的本质是URL与要为该URL调用的视图函数之间的映射表:你就是以这种方式告诉Django,对于客户端发来的某个URL调用哪一段逻辑代码对 ...

  9. Java知识总结----队列的使用

    首先我们要知道使用队列的目的是什么?一般情况下,如果是一些及时消息的处理,并且处理时间很短的情况下是不需要使用队列的,直接阻塞式的方法调用就可以了.但是,如果在消息处理的时候特别费时间,这个时候如果有 ...

  10. TCP粘包拆包基本解决方案

    上个小节我们浅析了在Netty的使用的时候TCP的粘包和拆包的现象,Netty对此问题提供了相对比较丰富的解决方案 Netty提供了几个常用的解码器,帮助我们解决这些问题,其实上述的粘包和拆包的问题, ...