Go中多协程协作之sync.Cond
Go中多协程协作之sync.Cond
ivansli1. 程序中的通信方式
GO语言中有句名言:“不要用共享内存来通信,而是使用通信来共享内存”。
编程语言中,通信方式分为进程间通信、线程间通信。
- 进程间通信,常用方式:
- 有名管道
- 无名管道
- 信号
- 共享内存
- 消息队列
- 信号灯集
- socket
- 线程间通信,常用方式:
- 信号量
- 互斥锁
- 条件变量
对于Go语言来说,Go程序启动之后对外是一个进程,内部包含若干协程,协程相当于用户态轻量级线程,所以协程的通信方式大多可以使用线程间通信方式来完成。
协程间通信方式,官方推荐使用channel,channel在一对一的协程之间进行数据交换与通信十分便捷。但是,一对多的广播场景中,则显得有点无力,此时就需要sync.Cond来辅助。
2. 什么是广播?
举个例子,上高中时,宿管老师每天早晨需要叫醒学生们去上课。有两种方法:①一个寝室一个寝室把学生叫醒 ②在宿舍楼安装广播,到起床时间,在广播上叫醒学生。显然,使用广播的方式效率更高。
编程中的广播可以理解为:多个操作流程依赖于一个操作流程完成后才能进行某种动作,这个被依赖的操作流程在唤醒所有依赖者时使用的一种通知方式。
在Go语言中,则可以使用sync.Cond来实现多个协程之间的广播通知功能。
3. sync.Cond
cond是sync包下面的一种数据类型,相当于线程间通信的条件变量方式。
1 | // Cond implements a condition variable, a rendezvous point |
该数据类型提供的方法有:
1 | type Cond |
对应源码追溯
1 | // Wait atomically unlocks c.L and suspends execution |
使用方法,代码示例:
1 | var locker sync.Mutex |
总结
在go中协程间通信的方式有多种,最常用的是channel。如果牵扯多个协程的通知,可以使用sync.Cond。
查看channel、sync.Cond源码会发现,它们有相似之处:
- 阻塞协程统一被封装在 sudog 结构里面
- channel 阻塞读/写时,用双向链表存储被阻塞导致等待唤醒的协程
- sync.Cond 使用带有头尾指针的单向链表存储被阻塞导致等待唤醒的协程
- 阻塞时都是使用gopark()进行协程的挂起操作
虽说有相似之处,但却有本质区别:
- channel 可用来在协程间传递数据
- sync.Cond 不可用来在协程间传递数据,主要用来进行协程的阻塞唤醒操作。如需要传递数据,则需要使用全局变量进行传递