goroutine 之间通信的通道就叫做 channel
1.1 创建使用 channel
定义一个 channel 类型:
创建一个 channel:
向一个 channel 发送数据:
接收一个 channel 发送的数据:
综合示例如下:
我们可以单独将 goroutine 抽离出来从而创建多个 goroutine,并建立多个 channel:
同时创建 goroutine 与生成一个 channel 的步骤可以合并为一个步骤,返回的对象为一个 channel:
定义channel类型时,我们可以规定其只能接受数据或者只能发送数据:
这样的操作通常用于返回一个仅可以接收数据的channel。
1.2 使用 BufferChanel
使用了 buffer channel 后,会创建一个缓冲区,不必等待 channel 的接收者,因此可以接受数条数据并不让系统发生死锁,如下的的程序是合法的:
创建缓冲区可以优化性能,但是与平常的使用无异。
1.3 使用 close() 函数关闭 channel
close(c chan<- Type)
0:
close()
可以使用 range 来简化代码:
不要通过共享内存来通信,要通过通信来共享内存。
2.使用 Channel 等待任务结束何为使用共享内存?
如:完成一件事情后将标识符 flag 设置为 true,某一方法监听 flag 的状态,从而获取通信信息。
2.1 在 goroutine 中创建 channel 向外通信
我们先来整理一下之前的代码,看其做了什么事情:
上面的代码会生成 10 个 Channel 分别向 10 个 Worker 发送数据(这是一个并发的过程),然后等待 1ms 之后不管 Woker 是否打印完毕,就停止发送数据。这个 1ms 的时间是我们自己“猜”的,显然我们更希望当 Woker 在完成工作后通知 Channel 可以停止发送数据。
利用 Channel 可以实现向外部通信,只需要在 goroutine 中创建一个 Channel ,在任务执行完成后,从外部通过接收这个 Channel 的信息即可判定当前的任务是否完成。
上述的流程由代码实现则为下:
但是我们会发现上述的结果打印小写字母与大写字母的流程是同步的,如果我们想将其改为非阻塞的,则需要将接收的两波由 Worker 发来的信息的操作集中放置在后面处理:
但是这样会造成死锁,这是因为由于接受 worker channel 的逻辑在后面,导致在 worker 发送第一批 channel 时还未有接收者就要发送第二批 channel 信息。我们可以将发送 channel 的步骤改为 goroutine 来发送信息:
2.2 使用 WaitGroup 等待 Channel 通信
Channelaync.WatiGroupWaitGroup.Done().Wait().Wait()WaitGroup.Done
利用 WaitGroup 的特性,我们可以重构如下代码:
由于 Channel 发数据与接受数据都是阻塞式的 ,我们可以由以下代码证实:
上述代码的大致流程如下:
在控制台可以看出 c1 与 c2 是固定按照顺序打印出的,如果在发送 channel 信号时,对发送顺序进行调转,则会出现死锁。其原因是因为先发送 c2 再发送 c1 的话,c2 会一直等待接收者。而 c2 的接收者此时尚未出现,先出现的是 c1 的接收者,然而在此时刻 c1 的信号尚未发出,所以就会导致程序陷入死锁:
但是使用 select 可以并行接收数据,会同时接收 c1 与 c2 的数据,但是仅接收先来的数据:
由于发送 channel 还是同步的顺序,所以上面的代码总是仅接收第一个 channel 数据。
如下是一个完整的使用 select 调度的示例:
以上称为 Go 语言的 csp 模型
4.传统同步机制(很少使用)WaitGroup
Mutex
Cond
4.1 Mutex
复写一个原子化的操作:
原文链接:https://blog.csdn.net/u012925833/article/details/101378096
更多Go知识干货内容
关注公众号:Golang在发光