+ goroutine 看一个需求
* 需求:要求统计 1 - 90000000000 的数字中,那些是素数?
+ 分析思路:
* 传统的方法,就是使用一个循环,循环的判断各个数是否是素数「效率很低」
* 使用并发或者并行的方式,将统计素数的任务分配个多个goroutine去完成,这时就会使用到goroutine「速度提升不少」
+ goroutine 基本介绍
> 进程和线程介绍
* 进程就是程序程序在操做系统中的一次执行过程,是系统进行资源分配和调度的基本单元
* 线程是进程的一个执行实例,是程序执行的最小单元,它是比进程更小的能独立运行的基本单元
* 一个进程能够建立和销毁多个线程,同一个进程中的多个线程能够并发执行
* 一个程序至少有一个进程,一个进程至少有一个线程
+ 并发和并行
* 多线程程序在单核上运行,就是并发
* 多线程程序在多核上运行,就是并行
+ 小结
* 并发: 由于是在一个cpu上, 好比有10个线程,每一个线程执行10ms(进行轮询操做),从人的角度看,好像这10个线程都在运行,可是从微观上看,
在某个时间点看,其实只有一个线程在执行,这就是并发。
* 并行: 由于是在多个cpu上(好比有10个cpu),好比有10个线程,每一个线程执行10ms(各自在不一样的cpu上执行),从人的角度看,这10个线程都在运行,可是从微观上看,在某个时间点看,也同时有10个线程在执行,这就是并行
+ go 协程和go主线程
* go主线程(有程序员直接称为线程/也能够理解程进程) : 一个go线程上,能够起多个协程,你能够这里理解,协程是轻量级的线程「编译器作优化」
+ go 协程的特色
* 有独立的栈空间
* 共享程序堆空间
* 调度由用户控制
* 协程是轻量级的线程
+ channel (管道)-看个需求
* 需求: 如今要计算1-200 的各个数的阶乘,而且把各个数的阶乘放入到map中,最后显示出来,要求使用goroutine完成
> 分析思路
* 使用goroutine来完成,效率高,可是会出现并发/并行安全问题
* 这里就提出了不一样的goroutine如何通讯的问题
> 代码实现
* 使用goroutine 来完成(看看使用goroutine并发完成会出现什么问题? 而后咱们去解决)
* 在运行某个程序时,如何知道是否存在资源竞争问题,方法很简单,在编译程序时。增长一个参数。 -race便可
* 代码实现见 goroutine 和 channel/goroutine_channel.go
> 上面的案例就出现了资源的竞争的问题
+ 不一样goroutine之间如何通信
* 全局变量的互斥锁
* 使用管道channel来解决
+ 使用全局变量加锁同步改进程序
* 由于没有对全局变量m加锁,所以会出现资源争夺问题,代码会出现错误,提示 fatal error: concurrent map writes
* 解决方案: 加入互斥锁
* 咱们的数的阶乘很大,结果会越界,能够将求阶乘改为sum+=uint64(i)
* 改进代码实现见 goroutine 和 channel/goroutine_channel_new.go
+ 为何须要channel
* 前面使用全局变量加锁同步来解决goroutine 的通信,但不完美
* 主线程在等待全部goroutine所有完成的时间很难肯定,咱们这里设置10s,仅仅是估算
* 若是主线程休眠时间长了,会加长等待时间,若是时间短了,可能还有goroutine处于工做状态,这时也会随主线程的退出而销毁
* 同步全局变量加锁同步来通信,也不利用多个协程对全局变量的读写操做
* 上面种种分析都在呼唤一个新的通信机制- channel
+ channel 的基本介绍
* channel本质就是一个数据结构-队列
* 数据是先进先出(FIFO: first in first out)
* 线程安全,多goroutine访问时,不须要加锁,就是说channel自己就是线程安全的
* channel有类型的,一个string的channel只能存放string类型数据
* channel 是线程安全的,多个协程操做同一个管道时,不会发生资源竞争问题
+ 定义/声明channel
* var 变量名 chan 数据类型
* 举例:
var intChan chan int (intChan 用户存放int数据)
var mapChan chan map[int]string (mapChan 用户存放map[int]string类型)
var perChan chan Person
var PerChan2 chan *Person
....
> 说明
- channel 是引用类型
- channel必须初始化才能写入数据,即make后才能使用
- 管道是有类型的,intChan只能写入整型int
+ 管道的初始化,写入数据到管道,从管道读取数据以及基本注意事项
* 代码实现见 goroutine 和 channel/channel.go
+ channel 使用的注意事项
* channel 中只能存放指定的数据类型
* channel 的数据放满后,就不能再放入了
* 若是从channel取出数据后,能够继续放入
* 在没有使用协程的状况下,若是channel数据取完了,在取,就会报dead lock
+ 读写channel 案例演示
* 案例见 goroutine 和 channel/intChan.go
* 案例见 goroutine 和 channel/mapChan.go
* 案例见 goroutine 和 channel/catChan.go
* 案例见 goroutine 和 channel/catChan2.go
- channel 的遍历和关闭
+ channel的关闭
* 使用内置函数close 能够关闭channel,当channel关闭后,就不能再向channel写数据了,可是仍然能够从该channel读取数据
* 案例演示见goroutine 和 channel/close.go
- channel 的遍历
+ channel支持for-range的方式进行遍历,请注意两个细节
* 再遍历时,若是channel没有关闭,则会出现deadlock的错误。
* 再遍历时。若是channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
- channel遍历和关闭的案例演示
+ 代码演示 goroutine 和 channel/channel_for_close.go
- 应用实例
+ 请完成gorouteine 和 channel协同工做的案例。具体要求
* 开启一个writeData协同,向管道intChan中写入50个整数
* 开启一个readData协程,从管道intChan中读取writeData写入的数据
* 注意: writeData 和 readData操做的是同一个管道
* 主线程须要等待writeData 和 readData协程都完成工做才能退出[管道]
* 案例代码 goroutine 和 channel/gorouteine.go
+ channel 使用细节和注意事项
* channel能够声明为只读,或者只协性质
案例 chan01.go
* channel 只读和只写的最佳实践案例
案例 chan02.go
* 使用select能够解决从管道取数据的阻塞问题
* goroutine 中使用recover,解决协程中出现panic,致使程序崩溃问题
* 若是咱们起一个协程,可是这个协程出现了panic,若是咱们没有捕获这个panic,就会形成整个程序崩溃,这时咱们
能够再goroutine中使用recover来捕获panic,进行处理,这样即便这个协程发生的问题,可是主线程仍然不受影响,能够
继续执行
.....详细见下面链接
细说 goroutine 和 channelgit