今天這篇筆記我們記錄sync包下面的Cond,Once和Pool
Cond
cond就是條件,當條件不滿足的時候等待Wait(),條件滿足后,繼續執行, 通過Signal()和Broadcast()來通知wait結束,繼續執行,我們先來看一個Signal通知的例子
func main() {
c := sync.NewCond(&sync.Mutex{})
queue := make([]interface{}, 0, 10)
removeFromQueue := func(delay time.Duration) {
time.Sleep(delay)
c.L.Lock()
queue = queue[1:]
fmt.Println("Removed from queue")
c.L.Unlock()
c.Signal()
}
for i := 0; i < 10; i++ {
c.L.Lock()
if len(queue) == 2 {
c.Wait()
}
fmt.Println("adding to the queue")
queue = append(queue, struct{}{})
go removeFromQueue(1 * time.Second)
c.L.Unlock()
}
fmt.Printf("queue length %d", len(queue))
}
這個程式是初始化了一個佇列, 當佇列的長度是2的時候,主goroutine等待, remove goruntine會洗掉佇列中的資料,然后通過Signal方法通知主goroutine結束等待,繼續執行添加,
這個程式執行的效果是這樣的
adding to the queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
Removed from queue
adding to the queue
可以看到當添加了兩個后,就等待了,后面remove一個就添加一個,最后還有兩個還沒有來得及remove,主goroutine就退出了,
如果把c.Signal去掉,那么就會報死鎖的錯誤, 因為主的goroutine等待了,子的gorountine也執行完了,就是都asleep了,就導致了報錯,
adding to the queue
adding to the queue
Removed from queue
Removed from queue
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [sync.Cond.Wait]:
sync.runtime_notifyListWait(0xc000064050, 0x0)
C:/Program Files/Go/src/runtime/sema.go:517 +0x152
sync.(*Cond).Wait(0xeded38?)
C:/Program Files/Go/src/sync/cond.go:70 +0x8c
main.main()
C:/Learn/go/helloworld/goroutinelearn/class4/main.go:30 +0x331
exit status 2
如果僅僅是通知一個等待一個,通過channel就可以做到, 如果要通知多個goroutine,那么就需要用到Broadcast. 我們把上面的例子稍微改動一下,讓多個洗掉的goroutine等待插入goroutine的通知,代碼如下所示
c := sync.NewCond(&sync.Mutex{})
queue := make([]interface{}, 0, 10)
var waitgroup sync.WaitGroup
removeFromQueue := func(delay time.Duration) {
c.L.Lock()
for len(queue) < 1 {
c.Wait()
}
queue = queue[1:]
fmt.Println("Removed from queue")
waitgroup.Done()
c.L.Unlock()
}
for i := 0; i < 2; i++ {
c.L.Lock()
go removeFromQueue(1 * time.Second)
go removeFromQueue(1 * time.Second)
c.L.Unlock()
}
waitgroup.Add(4)
for i := 0; i < 4; i++ {
c.L.Lock()
fmt.Println("adding to the queue")
queue = append(queue, struct{}{})
c.Broadcast()
c.L.Unlock()
}
waitgroup.Wait()
我們故意做成一次回圈呼叫兩個洗掉goroutine, 然后在洗掉里面當queue的數量為空的時候等待,
插入的時候,通過Broadcast來廣播這個訊息,程式運行結果
adding to the queue
adding to the queue
adding to the queue
Removed from queue
Removed from queue
Removed from queue
adding to the queue
Removed from queue
結果可以看出,添加了之后,隨后就能洗掉掉, 通知多個等待的goroutine,Broadcast還是比較有用,
Once
once 我們顧名思意就是一次, 只運行一次的意思, 這種對于只需要執行一次的功能會非常有用,
看下面的示例代碼
var count int
var lock sync.RWMutex
increment := func() {
lock.Lock()
count++
lock.Unlock()
}
decrement := func() {
lock.Lock()
fmt.Printf(" call decrement \n")
count--
lock.Unlock()
}
var once sync.Once
var increments sync.WaitGroup
increments.Add(100)
for i := 0; i < 100; i++ {
go func() {
defer increments.Done()
//increment()
once.Do(increment)
}()
}
once.Do(decrement)
increments.Wait()
fmt.Printf("Count is %d \n", count)
代碼的輸出為“Count is 1”, 我們通過once.Do 呼叫了increment 100次, 呼叫了 decrement 1次,但是實際上increment只被呼叫了一次, once.Do 是保證它只被呼叫一次,不是細到方法,不是說一個方法呼叫一次,而是所有的都只呼叫一次,
Pool
談到池,我們想到最多的就是執行緒池或者資料庫連接池, 也比較好理解,就是創建一個資源比較耗時的時候,我們通過池來快取一些資源,這樣就不用每次都創建,
看下面示例代碼
connPool := warmServiceConnCache()
connPool.Put(connPool.New())
connPool.Put(connPool.New())
for i := 1; i < 10; i++ {
conn1 := connPool.Get().(*Conncetor)
conn1.connect()
connPool.Put(conn1)
}
}
func warmServiceConnCache() *sync.Pool {
p := &sync.Pool{
New: connectToService,
}
return p
}
type Conncetor struct {
}
func (connector *Conncetor) connect() {
fmt.Println(" connecting")
}
func connectToService() interface{} {
fmt.Println(" creating new instance")
return new(Conncetor)
}
我們通過warmServiceConnCache 來回傳一個Pool, 然后往這個Pool 中放入兩個Connector,
通過Pool.Get()方法拿到創建的Connector, 呼叫了10次, 都是從Pool里面拿物件,而不需要創建10次,節省了資源,
程式的輸出結果如下
creating new instance
creating new instance
connecting
connecting
connecting
connecting
connecting
connecting
connecting
connecting
connecting
總結
這三個類,對于寫并發程式還是很有作用,光看不是很理解怎么使用,通過敲代碼,修改代碼,能夠對他們的用法有比較清晰的理解,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/537982.html
標籤:Go
