Golang sync.Cond源碼分析

cond的主要做用就是獲取鎖以後,wait()方法會等待一個通知,來進行下一步鎖釋放等操做,以此控制鎖合適釋放,釋放頻率,適用於在併發環境下goroutine的等待和通知。併發

針對Golang 1.9的sync.Cond,與Golang 1.10同樣。 源代碼位置:sync\cond.go。函數

結構體

type Cond struct {
	noCopy noCopy  // noCopy能夠嵌入到結構中,在第一次使用後不可複製,使用go vet做爲檢測使用

	// 根據需求初始化不一樣的鎖,如*Mutex 和 *RWMutex
	L Locker

	notify  notifyList  // 通知列表,調用Wait()方法的goroutine會被放入list中,每次喚醒,從這裏取出
	checker copyChecker // 複製檢查,檢查cond實例是否被複制
}

再來看看等待隊列notifyList結構體:ui

type notifyList struct {
	wait   uint32
	notify uint32
	lock   uintptr
	head   unsafe.Pointer
	tail   unsafe.Pointer
}

函數

NewCond

至關於Cond的構造函數,用於初始化Condatom

參數爲Locker實例初始化,傳參數的時候必須是引用或指針,好比&sync.Mutex{}或new(sync.Mutex),否則會報異常:cannot use lock (type sync.Mutex) as type sync.Locker in argument to sync.NewCond
你們能夠想一想爲何必定要是指針呢? 知道的能夠給我留言回答。指針

func NewCond(l Locker) *Cond {
	return &Cond{L: l}
}

Wait

等待自動解鎖c.L和暫停執行調用goroutine。恢復執行後,等待鎖c.L返回以前。與其餘系統不一樣,等待不能返回,除非經過廣播或信號喚醒。code

由於c。當等待第一次恢復時,L並無被鎖定,調用者一般不能假定等待返回時的條件是正確的。相反,調用者應該在循環中等待:隊列

func (c *Cond) Wait() {
    // 檢查c是不是被複制的,若是是就panic
	c.checker.check()
	// 將當前goroutine加入等待隊列
	t := runtime_notifyListAdd(&c.notify)
	// 解鎖
	c.L.Unlock()
	// 等待隊列中的全部的goroutine執行等待喚醒操做
	runtime_notifyListWait(&c.notify, t)
	c.L.Lock()
}

判斷cond是否被複制。it

type copyChecker uintptr

func (c *copyChecker) check() {
	if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&
		!atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) &&
		uintptr(*c) != uintptr(unsafe.Pointer(c)) {
		panic("sync.Cond is copied")
	}
}

Signal

喚醒等待隊列中的一個goroutine,通常都是任意喚醒隊列中的一個goroutine,爲何沒有選擇FIFO的模式呢?這是由於FiFO模式效率不高,雖然支持,可是不多使用到。ast

func (c *Cond) Signal() {
    // 檢查c是不是被複制的,若是是就panic
	c.checker.check()
	// 通知等待列表中的一個 
	runtime_notifyListNotifyOne(&c.notify)
}

Broadcast

喚醒等待隊列中的全部goroutine。class

func (c *Cond) Broadcast() {
    // 檢查c是不是被複制的,若是是就panic
	c.checker.check()
	// 喚醒等待隊列中全部的goroutine
	runtime_notifyListNotifyAll(&c.notify)
}

實例

package main

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

var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)

func main() {
	for i := 0; i < 40; i++ {
		go func(x int) {
			cond.L.Lock()         //獲取鎖
			defer cond.L.Unlock() //釋放鎖
			cond.Wait()           //等待通知,阻塞當前goroutine
			fmt.Println(x)
			time.Sleep(time.Second * 1)

		}(i)
	}
	time.Sleep(time.Second * 1)
	fmt.Println("Signal...")
	cond.Signal() // 下發一個通知給已經獲取鎖的goroutine
	time.Sleep(time.Second * 1)
	cond.Signal() // 3秒以後 下發一個通知給已經獲取鎖的goroutine
	time.Sleep(time.Second * 3)
	cond.Broadcast() //3秒以後 下發廣播給全部等待的goroutine
	fmt.Println("Broadcast...")
	time.Sleep(time.Second * 60)
}
相關文章
相關標籤/搜索