Golang:sync.Cond

条件变量与互斥锁配合使用

  • 条件变量是基于互斥锁的。用于协调想要访问共享资源的线程。当共享资源状态发生变化,用来通知被互斥锁阻塞的线程
  • 条件变量使用互斥锁初始化、并提供了三个方法:等待通知(Wait),单发通知(Signal)和广播通知(Broadcast)。
  • sync.Cond类型不是开箱即用的,需要通过构造函数NewCond创建它的指针值,NewCond需要一个sync.Locker类型的参数。
	// 0代表信箱是空的,1代表信箱是满的。
	var mailbox uint8
	// lock 代表信箱上的锁。
	var lock sync.Mutex
	// sendCond 代表专用于发信的条件变量。
	sendCond := sync.NewCond(&lock)
	// recvCond 代表专用于收信的条件变量。
	recvCond := sync.NewCond(&lock)
  • 调用Wait方法需要在互斥锁保护下进行;Signal方法和Broadcast方法需要在互斥锁解锁后调用。
  • 发送方代码:
	// 作为放信的一方,首先拿到写锁
	lock.Lock()
	// 拿到锁之后,如果箱子里的信还没被拿走,则等一会(等sendCond.Signal)
	for mailbox == 1 {
		sendCond.Wait()
	}
	// 等到箱子里的信被拿走了,重新放一封信
	mailbox = 1
	// 解锁并通知一个接收方过来拿信
	lock.Unlock()
	recvCond.Signal()
  • 接收方代码:
	// 被通知的接收方先加上锁
	lock.Lock()
	// 如果没有信,就等一会(等recvCond.Signal)
	for mailbox == 0 {
		recvCond.Wait()
	}
	// 等到箱子有信了,再把信拿走。
	mailbox = 0
	// 解锁并通知发送方再放一封
	lock.Unlock()
	sendCond.Signal()

Wait方法

  • 把当前goroutine加入到当前条件变量的通知等待队列。
  • 解锁当前条件变量基于的互斥锁。
  • 让当前goroutine处于等待状态,等到通知到来再决定是否唤醒。此时goroutine阻塞在Wait()这一行。
  • 通知到来并决定唤醒,那么唤醒之后重新锁定当前条件变量基于的互斥锁。
  • lock加锁 -> wait解锁等通知 -> 通知到来并唤醒 -> wait加锁 -> lock解锁

为什么要先加锁,再调用Wait

  • 因为Wait方法中会解锁:如果不加锁直接调用Wait,相当于直接解锁,会引发fatal error!
  • 为什么Wait方法中要解锁:如果不解锁,在互斥锁锁定情况下,当前goroutine阻塞,而其他goroutine都无法进入临界区(因为他们都拿不到锁),所以没有一个goroutine能够去修改共享资源的状态。放到上面的场景中就是,发送方加了互斥锁,如果Wait不解锁,那么接收方只能等最后发送方自己解锁了再去拿。

为什么要用for而不是if

  • 主要为了保险,可能存在以下情况:一个goroutine收到通知被唤醒,却发现共享资源依然不符合要求(第一次不符合要求就Wait了),那么应该继续等待下一次通知。
  • 多个goroutine关注同一个共享状态,比如有多个发信方,应该都等待发信,直到接收方把信取走。但是如果使用if,则发信方最多等待一次,就纷纷设置了mailbox=1然后退出了。造成的结果是,大家都发了,接收方只收到一封。
  • 共享资源的状态可能是多个,比如mailbox可能值是0,1,2,由于每次状态改变后结果只有一个。那么可能当前goroutine被唤醒,但是共享资源状态不是我想要的,那我就继续等待。
  • 还有一种可能,操作系统可能会在没有收到条件变量通知的情况下唤醒goroutine,如果没有循环,可能就出问题。

Signal方法和Broadcast方法

  • Signal只会唤醒一个等待的goroutine,Broadcast会唤醒所有。
  • Wait方法会将当前goroutine加入到通知队列队尾,而Signal方法总会从通知队列的队首开始,所以通常唤醒的是最早开始等待的。
  • 在发送通知时,如果没有等待的goroutine,该通知就没有任何用处。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值