同步之條件變量sync.Cond

同步之條件變量sync.Cond sync.Cond 的結構體ui

type Cond struct {
	// L is held while observing or changing the condition
	L Locker

	sema    syncSema
	waiters uint32 // number of waiters
	checker copyChecker
}

sync.Cond 的方法atom

//阻塞當前的goroutine
//方法Wait會自動的對與該條件變量關聯的那個鎖進行解鎖,而且使調用方所在的Goroutine被阻塞。
//一旦該方法收到通知,就會試圖再次鎖定該鎖。
//若是鎖定成功,它就會喚醒那個被它阻塞的Goroutine。
//不然,該方法會等待下一個通知,那個Goroutine也會繼續被阻塞。
func (c *Cond) Wait() {
	c.checker.check()
	if race.Enabled {
		race.Disable()
	}
        //原子遞增等待者計數,而後獲取信號量進入休眠
	atomic.AddUint32(&c.waiters, 1)
	if race.Enabled {
		race.Enable()
	}
	c.L.Unlock()
	runtime_Syncsemacquire(&c.sema)
	c.L.Lock()
}

func (c *Cond) Signal() {
}

func (c *Cond) Broadcast() {
}

先看一個簡單的用法,這樣一個場景: 在控制檯輸入enter,做爲一個發送通知的信號,使阻塞的goroutine繼續執行。code

package main

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

func main() {

	locker := sync.Mutex{}
	cond := sync.NewCond(&locker)

	go func() {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		cond.Signal()//當鍵盤輸入enter後,發出通知信號
		fmt.Println("signal...")
	}()

	go func() {
		cond.L.Lock() //首先進行鎖定,與之關聯的條件變量的鎖定
		fmt.Println("wait before...")
		//等待Cond消息通知
		cond.Wait()
		fmt.Println("wait end...")
		cond.L.Unlock()
	}()

	time.Sleep(10 * time.Second)
	fmt.Println("exit...")
}

運行結果,同步

wait before...

signal...
wait end...
exit...

在打印出wait before...後,而後在控制檯輸入空格,最後wait end。 問題來了,若是在wait 以前輸入了enter怎麼辦,也就是說提早發出了信號怎麼辦?以下代碼,it

package main

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

func main() {
	var wg sync.WaitGroup

	locker := sync.Mutex{}
	cond := sync.NewCond(&locker)

	go func() {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		cond.Signal()//當鍵盤輸入enter後,發出通知信號
		fmt.Println("signal...")
	}()

	time.Sleep(5 * time.Second)
	fmt.Println("sleep end...")

	wg.Add(1)
	go func() {
		defer wg.Done()
		cond.L.Lock() //首先進行鎖定,與之關聯的條件變量的鎖定
		fmt.Println("wait before...")
		//等待Cond消息通知
		cond.Wait()
		fmt.Println("wait end...")
		cond.L.Unlock()
	}()

	wg.Wait()
	fmt.Println("exit...")
}

運行結果,io

signal...
sleep end...
wait before...
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc82000a28c)
	/usr/local/go/src/runtime/sema.go:47 +0x26
sync.(*WaitGroup).Wait(0xc82000a280)
	/usr/local/go/src/sync/waitgroup.go:127 +0xb4
main.main()
	/Users/xinxingegeya/gogogo/ucar.com/cond/hello.go:36 +0x245

goroutine 7 [semacquire]:
sync.runtime_Syncsemacquire(0xc820010290)
	/usr/local/go/src/runtime/sema.go:241 +0x201
sync.(*Cond).Wait(0xc820010280)
	/usr/local/go/src/sync/cond.go:63 +0x9b
main.main.func2(0xc82000a280, 0xc820010280)
	/Users/xinxingegeya/gogogo/ucar.com/cond/hello.go:31 +0x159
created by main.main
	/Users/xinxingegeya/gogogo/ucar.com/cond/hello.go:34 +0x237
exit status 2

Process finished with exit code 1

在sleep end...以前輸入enter,這時會發出信號,當sleep 結束以後,當執行接下來的goroutine時就會發生錯誤,即便wait了,也不會發出信號了。怎麼才能避免這種狀況呢?推薦用法以下,ast

package main

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

func main() {
	var wg sync.WaitGroup

	locker := sync.Mutex{}
	cond := sync.NewCond(&locker)

	var condition bool = false

	go func() {
		os.Stdin.Read(make([]byte, 1)) // read a single byte
		cond.L.Lock()
		cond.Signal()    //當鍵盤輸入enter後,發出通知信號
		condition = true //把條件變量設爲true,表示發送過信號
		fmt.Println("signal...")
		cond.L.Unlock()
	}()

	time.Sleep(5 * time.Second)
	fmt.Println("sleep end...")

	wg.Add(1)
	go func() {
		defer wg.Done()
		cond.L.Lock() //首先進行鎖定,與之關聯的條件變量的鎖定
		fmt.Println("wait before...")
		//等待Cond消息通知
		for !condition {
			//當條件爲真時,不會發生wait
			fmt.Println("wait...")
			cond.Wait()
		}
		fmt.Println("wait end...")
		cond.L.Unlock()
	}()

	wg.Wait()
	fmt.Println("exit...")
}

在sleep 以前輸入enter,import

signal...
sleep end...
wait before...
wait end...
exit...

在sleep以後輸入enter,變量

sleep end...
wait before...
wait...

signal...
wait end...
exit...

=======END=======方法

相關文章
相關標籤/搜索