Golang语言标准库 sync 包的 Cond 怎么使用?

简介: Golang语言标准库 sync 包的 Cond 怎么使用?

image.jpeg

01

介绍


在 Go 语言中,Cond 实现一个条件变量,协助解决等待或通知事件场景的并发执行问题,通常用于等待某个条件的一组 goroutine。这个条件需要一组 goroutine 共同协作完成,如果条件为 false,所有等待这个条件的 goroutine 将会被阻塞,当这个条件变为 true 时,所有等待这个条件的其中一个 goroutine 或者所有 goroutine 会被唤醒。


02

基本用法


通过阅读源码,我们可以发现 Cond 关联一个 Locker L(通常是 *Mutex 或 *RWMutex),在更新条件和调用 Wait 方法时必须持有该锁 L。并且,首次使用后不得复制 Cond。通常,使用 NewCond 函数创建一个 Cond。


// A Cond must not be copied after first use.
type Cond struct {
  noCopy noCopy
  // L is held while observing or changing the condition
  L Locker
  notify  notifyList
  checker copyChecker
}
// NewCond returns a new Cond with Locker l.
func NewCond(l Locker) *Cond {
  return &Cond{L: l}
}
func (c *Cond) Wait() {
  c.checker.check()
  t := runtime_notifyListAdd(&c.notify)
  c.L.Unlock()
  runtime_notifyListWait(&c.notify, t)
  c.L.Lock()
}
func (c *Cond) Signal() {
  c.checker.check()
  runtime_notifyListNotifyOne(&c.notify)
}
func (c *Cond) Broadcast() {
  c.checker.check()
  runtime_notifyListNotifyAll(&c.notify)
}


Cond 只有 3 个方法,分别是 Wait、Signal 和 Broadcast。下面分别介绍一下这 3 个方法。

  • Wait:把调用者放入等待队列中并阻塞,直到被 Signal 方法或 Broadcast 方法在等待队列中移除并唤醒。调用 Wait 方法之前,调用者必须持有锁。
  • Signal:调用者将等待队列中的 goroutine 移除第一个,并唤醒被移除的 goroutine。
  • Broadcast:调用者将等待队列中的所有 goroutine 全部移除,并全部唤醒。


了解了 Cond 的 3 个方法,我们通过实现一个「学生报名参加课外活动」的简单示例,演示如何使用 Cond。


640.png


其中,需要注意的是 Wait 方法。调用者在调用 Wait 方法之前,必须持有锁,并且每次调用都要检查辅助条件变量 count。


03

实现原理


通过阅读 Part 02 的源码,可以发现 Cond 实现非常简单,主要是通过 Wait 方法将调用者放入一个等待队列中并阻塞,其他 goroutine 去检查或更新条件变量,然后通过调用 Signal 方法或 Broadcast 方法唤醒等待队列中的一个或全部 goroutine。


04

踩坑


使用 Cond,最容易踩的坑就是调用 Wait 方法之前,调用者没有持有锁或没有检查辅助条件。


在 Part 02 的示例代码中,假如把调用 Wait 方法前后的加锁和释放锁的代码注释掉,运行代码会导致程序 panic。原因是调用 Wait 方法,会先把调用者放入等待队列中,然后释放锁。此时如果在未持有锁时调用释放锁的方法,就会导致程序 panic。


还有就是等待队列中的 goroutine 被唤醒,并不代表条件满足,还要再次检查辅助条件是否满足。


05

总结


本文开篇介绍了 Cond 的用途,然后结合源码介绍了 Cond 的实现和 3 个方法,并通过一个「学生报名参加课外活动」的模拟示例演示了 Cond 的基本使用,最后列举了一个非常容易踩的「坑」。





目录
相关文章
|
20天前
|
Java 编译器 Go
【Golang】(1)Go的运行流程步骤与包的概念
初次上手Go语言!先来了解它的运行流程吧! 在Go中对包的概念又有怎样不同的见解呢?
64 4
|
20天前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
73 1
|
3月前
|
设计模式 Kubernetes Go
​​什么是Golang项目的“主包精简,逻辑外置”?​
“主包精简,逻辑外置”是Go语言项目的一种设计原则,强调将程序入口保持简单,核心逻辑拆分至其他包,以提升代码可维护性、可测试性及扩展性,适用于CLI工具、Web服务等场景。
99 7
|
5月前
|
Go
在golang中发起http请求以获取访问域名的ip地址实例(使用net, httptrace库)
这只是追踪我们的行程的简单方法,不过希望你跟着探险家的脚步,即使是在互联网的隧道中,也可以找到你想去的地方。接下来就是你的探险之旅了,祝你好运!
247 26
|
7月前
|
Go 开发者
go-carbon v2.6.0 重大版本更新,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持
137 3
|
8月前
|
网络协议 测试技术 Linux
Golang 实现轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库
gev 是一个基于 epoll 和 kqueue 实现的高性能事件循环库,适用于 Linux 和 macOS(Windows 暂不支持)。它支持多核多线程、动态扩容的 Ring Buffer 读写缓冲区、异步读写和 SO_REUSEPORT 端口重用。gev 使用少量 goroutine,监听连接并处理读写事件。性能测试显示其在不同配置下表现优异。安装命令:`go get -u github.com/Allenxuxu/gev`。
164 0
|
11月前
|
JSON Go 开发者
go-carbon v2.5.0 发布,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。
230 4
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
533 4
Golang语言之管道channel快速入门篇
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
214 4
Golang语言文件操作快速入门篇
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
457 3
Golang语言之gRPC程序设计示例

热门文章

最新文章

推荐镜像

更多