sync.Cond

go version go1.13.4 linux/amd64

用于同步多个协程间的条件状态
比如在消费者-生产者模型中,只有在队列中有资源时,消费者才可以消费,只有在队列还能够放入资源时,生产者才可以放入资源。消费者协程和生产者协程之间需要同步队列的状态,这里的状态同步可以用sync.Cond来实现。

源码分析

// Wait atomically unlocks c.L and suspends execution
// of the calling goroutine. After later resuming execution,
// Wait locks c.L before returning. Unlike in other systems,
// Wait cannot return unless awoken by Broadcast or Signal.
// Wait方法释放锁,并阻塞协程执行。满足条件解除阻塞后,当前协程需要获得锁然后Wait方法返回。
//
// Because c.L is not locked when Wait first resumes, the caller
// typically cannot assume that the condition is true when
// Wait returns. Instead, the caller should Wait in a loop:
// 由于解除阻塞后,当前协程不一定能马上获得锁,因此返回后需要再次检查条件,所以通常
// 使用循环。
//    c.L.Lock()
//    for !condition() {
//        c.Wait()
//    }
//    ... make use of condition ...
//    c.L.Unlock()
//
func (c *Cond) Wait() {
	c.checker.check()
	t := runtime_notifyListAdd(&c.notify)
	c.L.Unlock() // 释放锁
	runtime_notifyListWait(&c.notify, t) // 等待满足条件,解除阻塞
	c.L.Lock() // 获取锁
}

// Signal wakes one goroutine waiting on c, if there is any.
//
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Signal() {
	c.checker.check()
	runtime_notifyListNotifyOne(&c.notify)
}

// Broadcast wakes all goroutines waiting on c.
//
// It is allowed but not required for the caller to hold c.L
// during the call.
func (c *Cond) Broadcast() {
	c.checker.check()
	runtime_notifyListNotifyAll(&c.notify)
}

示例
这里用container/list简单模拟队列
这里只是简单用Cond实现生产者-消费者模型,实际上生产者-消费者模型由channel实现更加方便合理。

package main

import (
	"container/list"
	"fmt"
	"math/rand"
	"sync"
	"time"
)

var (
	lock  = new(sync.Mutex)
	cond  = sync.NewCond(lock)
	queue = NewQueue(5)
	done  = make(chan bool)
)

func main() {
	for i := 0; i < 3; i++ {
		go func(i int) {
			for {
				lock.Lock()
				for queue.IsEmpty() {
					cond.Wait()
				}
				fmt.Printf("消费者[%d]:%s\n", i, queue.Take().Data)
				cond.Signal()
				lock.Unlock()
			}
		}(i)
	}
	for i := 0; i < 5; i++ {
		go func(i int) {
			for {
				lock.Lock()
				for queue.IsFull() {
					cond.Wait()
				}
				fmt.Printf("生产者[%d]\n", i)
				queue.Put(&Product{Data: "hello, world"})
				cond.Signal()
				lock.Unlock()
				r := rand.Int31n(3)
				time.Sleep(time.Duration(r) * time.Second)
			}
		}(i)
	}
	<-done
}

type Product struct {
	Data string
}

type Queue struct {
	l   *list.List
	Cap int
}

func NewQueue(capacity int) *Queue {
	return &Queue{l: list.New(), Cap: capacity}
}

func (q *Queue) Put(p *Product) {
	q.l.PushBack(p)
}

func (q *Queue) Take() *Product {
	t := q.l.Front()
	defer q.l.Remove(t)
	return t.Value.(*Product)
}

func (q *Queue) IsEmpty() bool {
	return q.l.Len() == 0
}

func (q *Queue) IsFull() bool {
	return q.l.Len() == q.Cap
}

这是一种通用的编程模式:

获取锁
while(条件不满足){
	释放锁
	线程/协程阻塞等待,直到收到条件满足的通知
	获取锁
}
临界区操作
释放锁

java中可以采用synchronized、wait、notify和notifyAll实现,见synchronized

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值