1. 程式人生 > >go條件變數sync.Cond的使用和思考

go條件變數sync.Cond的使用和思考

原始碼採用1.9版本;sync包的Cond,條件變數;在我看來,主要是採用他的wait()方法,來控制被阻塞的go程何時去競爭鎖;我暫且叫它“雙開關控制”(歡迎大神斧正):
廢話少說,來個小例子吧:

package main
import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var lc = new(sync.Mutex)
    //這個locker 為啥傳入一個引用?
    var cond = sync.NewCond(lc)
    for i := 0; i < 3; i++ {
        go func(x
int) { cond.L.Lock() defer cond.L.Unlock() cond.Wait() fmt.Println(x) }(i) } //睡眠一會,確保下面的Signal()能通知到一個(難道可能通知不到?) time.Sleep(2*time.Second) cond.Signal() cond.Broadcast() time.Sleep(2*time.Second) }

執行結果:

0
2
1

上面例子中有兩個問題,我們帶著問題,檢視Cond的一個重要方法:

func (c *Cond) Wait() {
    //檢查cond是否被拷貝
    c.checker.check()
    //將獲得鎖的那個go程加入等待佇列
    t := runtime_notifyListAdd(&c.notify)
    //釋放鎖(可見呼叫之前要有c.L.lock)
    c.L.Unlock()
    //go程的等待佇列等待喚醒,這個操作是阻塞的,除非本go程被喚醒
    runtime_notifyListWait(&c.notify, t)
    //外部記得釋放
    c.L.Lock()
}

為啥傳入一個引用?通過wait方法我看到c.L 的操作,如果不是指標變數,也就是發生鎖得拷貝,將導致鎖不統一,從而發生死鎖;
難道可能通知不到?

如果不等待至少一個go程加入等待佇列,此時呼叫cond.Signal() 通知,是沒有作用的;並且我們發現,go程加入等待佇列,需要競爭鎖,其實我也沒有想明白,為啥要加鎖,也許是等待佇列的操作是執行緒不安全的。