Go基礎之鎖的初識
當我們的程序就一個線程的時候是不需要用到鎖的,但是通常我們實際的代碼不會是單個線程的,所有這個時候就需要用到鎖了,那么關于鎖的使用場景主要涉及到哪些呢?
- 當我們多個線程在讀相同的數據的時候則是需要加鎖的
- 當我們的程序既有讀又有寫的時候更是需要加鎖的
- 當我們有多個線程在寫的時候同樣也是需要加鎖
互斥鎖
互斥鎖:同一個時刻只有一個線程能夠拿到鎖
我們先通過一個例子來演示,如果當多個線程同時更改一個變量,結果會是怎么樣
不加鎖版本
package mainimport ("sync""fmt" )var (//lock sync.Mutexcount intw sync.WaitGroup //用于等待子線程執行完之后退出 )func main() {w.Add(1) // 在調用線程前執行w.addgo func(){for i:=0;i<100000;i++{count++}w.Done() //執行完 執行w.Done}()for i :=0;i<100000;i++{count++}w.Wait() // 最后執行w.wait等待所有的線程執行完畢fmt.Println(count)}
當我們運行多次就可以發現,最后的結果基本不可能是我們先看到的:200000
我們修改代碼代碼需要加鎖保護的地方加上鎖,并且這里加的是互斥鎖,修改后的代碼為:
package mainimport ("sync""fmt" )var (lock sync.Mutexcount intw sync.WaitGroup //用于等待子線程執行完之后退出 )func main() {w.Add(1) // 在調用線程前執行w.addgo func(){for i:=0;i<100000;i++{lock.Lock()count++lock.Unlock()}w.Done() //執行完 執行w.Done}()for i :=0;i<100000;i++{lock.Lock()count++lock.Unlock()}w.Wait() // 最后執行w.wait等待所有的線程執行完畢fmt.Println(count)}
這次當我們多次運行的時候,就能保證我們每次都能看到我們想要的值:200000
接下來看讀寫鎖
讀寫鎖
讀寫鎖主要用到讀多寫少的場景
讀寫鎖分為:讀鎖和寫鎖
如果自己設置了一個寫鎖,那么其他讀的線程以及寫的線程都拿不到鎖,這個時候和互斥鎖的功能相同
如果自己設置了一個讀鎖,那么其他寫的線程是拿不到鎖的,但是其他讀的線程都是可以拿到這個鎖
我們把上面的例子代碼進行更改:
package mainimport ("sync""fmt" ) var (rwlock sync.RWMutexw sync.WaitGroupcount int )func main() {w.Add(1)go func(){for i:=0;i<1000000;i++{rwlock.Lock() // 這里定義了一個寫鎖count++rwlock.Unlock()}w.Done()}()for i:=0;i<1000000;i++{rwlock.Lock() // 這里定義了一個寫鎖count++rwlock.Unlock()}w.Wait()fmt.Println(count) }
通過設置寫鎖,我們同樣可以實現數據的一致性
下面是一個讀鎖的使用例子:
package mainimport ("sync""fmt" )var (rwlock sync.RWMutexw sync.WaitGroupcount int )func main() {w.Add(1)go func(){for i:=0;i<1000000;i++{rwlock.Lock() // 這里定義了一個寫鎖count++rwlock.Unlock()}w.Done()}()for i:=0;i<16;i++{w.Add(1)go func(){rwlock.RLock() //這里定義了一個讀鎖fmt.Println(count)rwlock.RUnlock() //釋放讀鎖w.Done()}()}w.Wait()fmt.Println(count) }
Go中的原子操作
原子操作,我們則不需加鎖,也能保證數據的一致性
并且如果只是計算,那么原子操作則是最快的
實例代碼:
package mainimport ("sync"//"time""sync/atomic""fmt" )var (w sync.WaitGroupcount int32 )func main() {w.Add(1)//start := time.Now().UnixNano()go func() {for i:=0;i<1000000;i++{atomic.AddInt32(&count,1)}w.Done()}()for i:=0;i<1000000;i++{atomic.AddInt32(&count,1)}w.Wait()//end := time.Now().UnixNano()//fmt.Println((end- start)/1000/1000)fmt.Println(count) }
?