來源:blog.csdn.net/weixin_61594803 1.SQL數據脫敏實現 MYSQL(電話號碼,身份證)數據脫敏的實現 -- CONCAT()、LEFT()和RIGHT()字元串函數組合使用,請看下麵具體實現 -- CONCAT(str1,str2,…):返回結果為連接參數產生的字元 ...
今天這篇筆記我們記錄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
總結
這三個類,對於寫併發程式還是很有作用,光看不是很理解怎麼使用,通過敲代碼,修改代碼,能夠對他們的用法有比較清晰的理解。