生产者-消费者模式即所谓的Fan-in设计模式,这里建立两个channel,一个由多个生产者将生产的数据写入;另外一个由一个消费者读出。
线程同步由主线程借助两个channel完成,其本质是利用了阻塞实现同步。
生产者
一个大loop将每个新的i值循环写入。
|
|
消费者
|
|
线程同步
从写入channel rang loop的读取,塞入输出channel。
|
|
这里利用了阻塞channel的特性:如果读取不到则阻塞,可以加入超时。
死锁的产生
现象:
|
|
死锁原因1:线程都在接收导致的死锁
将生产者的loop变成有限次执行后退出。
|
|
将会导致main线程和reader线程阻塞:
|
|
- Step1: 由于producer线程退出,ch没有人给写入数据,此时为空;
- Step2: 此时线程goroutine 1从ch中尝试读取数据,发现读不到,因此阻塞在该channel上,该线程ID为1,即main线程,导致out中无法写入数据,此时为0:
|
|
- Step3:而读线程goroutine 17不知情(该线程由main创建,ID为17),仍从out读取数据,导致reader最终也处于了阻塞状态。
|
|
死锁原因2:线程都在发送导致的死锁
将消费者行为改为对数据不做消费:
|
|
导致多个线程都往channel中写数据,这里channel是不一致的(可以一致):
|
|
- Step1: producer生产一个数据,塞入ch;
- Step2:线程goroutine 1为main线程, 从ch中拿走一个,往out中写数据:
|
|
out无人消费,导致写了一个就已经满了,因此阻塞。
- Step3:线程goroutine 5为producer1和线程goroutine 6此时仍在往ch中写数据:
|
|
发现ch数据没有被消费,写不进去而阻塞。
如何通知退出循环
Close行为与loop之间通过s.closing
|
|
- 服务的Loop循环监听该channel的请求
- client往channel发送一个请求:退出和响应该error
- 差点
通过一个channel发送消息给待关闭的thread,将channel类型设置为channel,这样loop可以将错误的原因发送过来:
|
|
Loop的处理:取走error并退出
|
|
select 和 nil channel
对于nil channel,无论发送与接收操作都会阻塞;
select永远不会选择一个阻塞的case.
nil channel与空channle的区别:?
参考代码
|
|
获取goroutine的GID:
|
|