Golang Condソース分析
condの主な役割は、ロックを取得した後、wait()メソッドが次のロック解除などの操作を行う通知を待つことであり、ロックの適切な解放、解放頻度を制御し、同時環境でのgoroutineの待機と通知に適している.
Golang 1.9のsync.Condは、Golang 1.10と同じです.ソースの場所:synccond.go.
待ち行列
パラメータはLockerインスタンスの初期化であり、パラメータを渡すときは&syncなどの参照またはポインタでなければならない.Mutex{}またはnew(sync.Mutex)です.そうしないと、
自動ロック解除c.Lを待ち、goroutineの呼び出しを一時停止します.リカバリ実行後、ロックc.Lが戻るまで待機します.他のシステムとは異なり、ブロードキャストまたは信号によって起動しない限り、待機は戻らない.
なぜならc.最初のリカバリを待つ場合、Lはロックされず、呼び出し元は通常、戻りを待つ条件が正しいとは仮定できません.逆に、呼び出し元はループで待機する必要があります.
condがコピーされているかどうかを判断します.
起動待ちキューのgoroutineは、一般的に任意の起動キューのgoroutineですが、なぜFIFOのモードを選択しなかったのでしょうか.これは、FiFOモードは効率的ではなく、サポートされているものの、あまり使われていないためです.
待機キュー内のすべての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)
}