首頁 > 軟體

Go語言開發保證並行安全範例詳解

2022-09-02 18:02:31

什麼是並行安全?

高並行場景下,程序、執行緒(協程)可能會發生資源競爭,導致資料髒讀、髒寫、死鎖等問題,為了避免此類問題的發生,就有了並行安全。

這裡舉一個簡單的例子:

 var data int 
 go func() {
   data++ 
 }() 
 if data == 0 { 
   fmt.Printf("the value is %v.n", data) 
 }

在這段程式碼中

第2行go關鍵字開啟了一個新的協程,來執行data++操作

第5行,對data變數進行了讀取判斷的操作

以上兩部是由2個不同執行緒/協程執行,且沒有任何措施保證執行順序,所以執行結果是不確定的。

  • 沒有輸出。(第3行是在第5行之前執行的)
  • 輸出 the value is 0。(第5行和第6行在第3行之前執行)
  • 輸出 the value is 1。(第5行在第3行之前執行,但第3行在第6行之前執行)

Go如何保證並行安全

目前瞭解到的,大概有這3種,Mutex、Channel、Atomic

Mutex

加鎖應該是最常見的並行控制方法,一般分成兩種,樂觀鎖和悲觀鎖

鎖是由作業系統的排程器來實現的,鎖通常用來保護一段邏輯,

悲觀鎖

悲觀鎖是一種悲觀思想,它總認為最壞的情況可能會出現。不管意料之外的結果是否會發生,只要存在發生的可能,就在操作這個資源之前先上鎖。例如互斥鎖讀寫鎖都是悲觀鎖。

在go中,除了automic,其它都是悲觀鎖

悲觀鎖應該都是由作業系統的排程器來實現的,通常用來保護一段邏輯,主要是通過阻塞其它執行緒,保證當前時刻只有一個執行緒在對資源進行操作,因此效能相對較差,浪費了計算機多核的優勢。

樂觀鎖

樂觀鎖的思想與悲觀鎖的思想相反,它總認為資源和資料不會被別人所修改,所以讀取不會上鎖,但是樂觀鎖在進行寫入操作的時候會判斷當前資料是否被修改過

樂觀鎖的實現方案主要包含CAS版本號機制

樂觀鎖適用於多讀的場景,可以提高吞吐量。

版本號機制

通過在資料表中,增加一個版本號欄位,當資料發生更新時,版本號值發生改變。 例如一個執行緒A想要更新變數s的值,在讀取s的值的同時讀取版本號,在提交更新時,用之前讀到的版本號值與當前的版本號值進行比對,當且僅當版本號值一致時,才會觸發更新,否則不斷進行重試,直到更新成功。

CAS

CAS全名為Compare And Swap,即比較與轉換,是一種有名的無鎖演演算法。在不使用鎖的情況下,實現多執行緒之間的變數同步,也就是在沒有執行緒被阻塞的情況下實現變數的同步,所以也叫非阻塞同步,

互斥鎖

GO使用Sync包的Mutex型別來實現互斥鎖,它能保證同時只有一個goroutine可以存取資源。

func sample() {
	var l sync.Mutex
	l.Lock()
        defer l.Unlock()
	// so something
}

讀寫互斥鎖

GO使用Sync包的RWMutex型別來實現互斥鎖。當我們去並行的讀取一個資源,只要資料沒有發生寫入,是沒必要加鎖的。因此讀多寫少的情況下,使用讀寫互斥鎖是更好的選擇,效能更好。

讀寫鎖分為兩種:讀鎖和寫鎖。

當一個goroutine獲取讀鎖之後,其他的goroutine如果是獲取讀鎖可以順利獲得,如果是獲取寫鎖就會等待;

當一個goroutine獲取寫鎖之後,其他的goroutine無論是獲取讀鎖還是寫鎖都會等待。

package main
import (
	"fmt"
	"sync"
	"time"
)
var (
	wg     sync.WaitGroup
	rwlock sync.RWMutex
)
func write() {
	rwlock.Lock() // 加寫鎖
	time.Sleep(10 * time.Millisecond)
	rwlock.Unlock() // 解寫鎖
	wg.Done()
}
func read() {
	rwlock.RLock() // 加讀鎖
	time.Sleep(time.Millisecond)
	rwlock.RUnlock() // 解讀鎖
	wg.Done()
}
func main() {
	start := time.Now()
	//讀多
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go read()
	}
	//寫少
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go write()
	}
	wg.Wait()
	end := time.Now()
	fmt.Println(end.Sub(start))
}

以上就是Go語言開發保證並行安全範例詳解的詳細內容,更多關於Go保證並行安全的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com