go语言sync.Cond

sync.Cond 是一个事件通知,类似于java中的conditional 或者 wait/notify 机制。

它有一个重要的作用是,协程之间通过锁进行协调的时候,其中一个协程等待的时候,可以释放锁和资源,并且能够及时完成准备工作。

比如经典的生产者消费,例如有一个队列,只有大小为1,生产者需要等待消费者完成消费,才能继续存放。

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	cond := sync.NewCond(&sync.Mutex{})
	msg := make([]string, 1)
	var sended bool
	go func() {
		for {
			time.Sleep(time.Second)
			cond.L.Lock()
			for !sended {
				cond.Wait()
			}
			fmt.Printf("get msg %s\n" , msg[0])
			sended = false
			cond.Signal()
			cond.L.Unlock()
		}
	}()
	go func() {
		for {
			time.Sleep(time.Second)
			cond.L.Lock()
			for sended {
				cond.Wait()
			}
			s := fmt.Sprint("time is ", time.Now().String())
			msg[0]= s
			fmt.Printf("put in msg:%s\n", s)
			sended = true
			cond.Signal()
			cond.L.Unlock()
		
		}
	}()
	time.Sleep(time.Second* 50)
}

它的模式是

cond.L.Lock()
for 判断条件 {
	cond.Wait()
}
//执行任务
cond.L.Unlock()

另外一个地方当条件满足时,就会cond.Signal(),但是signal方法只会唤醒一个协程,如果真实场景下是多个协程中一部分需要被唤醒,就**必须要用 **cond.Broadcast(), 相当于notifyAll()。

通常唤醒的地方,也是在使用临界区的资源,所以唤醒的地方也会写成如下模式

cond.L.Lock()
// 执行任务
cond.Broadcast()
cond.L.Unlock()

相比较于 notify 的用法宽松的地方是, Broadcast 和Signal 方法不需要放在锁内。而wait条件因为已经包装在for循环内,就可以放心的Broadcast

由于上面的示例是互相唤醒,因此写法如下:

cond.L.Lock()
for 判断条件 {
	cond.Wait()
}
cond.Broadcast()
cond.L.Unlock()

虽然channel也可以用于这个场景,但是cond 在一些简化的场景下,可以取得较好的性能。

猜你喜欢

转载自www.cnblogs.com/shalk/p/13401403.html
今日推荐