Golang Condソース分析


condの主な役割は、ロックを取得した後、wait()メソッドが次のロック解除などの操作を行う通知を待つことであり、ロックの適切な解放、解放頻度を制御し、同時環境でのgoroutineの待機と通知に適している.
Golang 1.9のsync.Condは、Golang 1.10と同じです.ソースの場所:synccond.go.

こうぞうたい

type Cond struct {
    noCopy noCopy  // noCopy , , go vet 

    //  , *Mutex   *RWMutex
    L Locker

    notify  notifyList  //  , Wait() goroutine list , , 
    checker copyChecker //  , cond 
}

待ち行列notifyListの構造体を見てみましょう.
type notifyList struct {
    wait   uint32
    notify uint32
    lock   uintptr
    head   unsafe.Pointer
    tail   unsafe.Pointer
}

関数#カンスウ#


NewCond

Condに相当する構造関数は、Condを初期化するために使用される.
パラメータは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が戻るまで待機します.他のシステムとは異なり、ブロードキャストまたは信号によって起動しない限り、待機は戻らない.
なぜなら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がコピーされているかどうかを判断します.
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モードは効率的ではなく、サポートされているものの、あまり使われていないためです.
func (c *Cond) Signal() {
    //  c , panic
    c.checker.check()
    //   
    runtime_notifyListNotifyOne(&c.notify)
}

Broadcast


待機キュー内のすべてのgoroutineを起動します.
func (c *Cond) Broadcast() {
    //  c , panic
    c.checker.check()
    //  goroutine
    runtime_notifyListNotifyAll(&c.notify)
}

≪インスタンス|Instance|emdw≫

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)
}