内容简介:在某个时间点看,其实只有一个线程在执行,这就是并发。分析思路代码实现
-
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
.....详细见下面连接
微信扫码关注站长公众号,和站长交流学习
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。