作為Go 官方套件的一部分,sync 套件有下面這段聲明:
sync 套件提供了基本的同步原語,例如互斥鎖。除了 Once 和 WaitGroup 類型之外,大多數其他類型都是為底層函數庫準備的。透過 channel 和通訊更好地完成更高層級的同步.
在你能找到的關於允許並發存取的絕大多數例子中,很多都是使用互斥鎖來解決問題。然而,幾乎很少有範例給我們看如何使用 channel 提供同步機制。所以,這篇文章我們就來討論下。
為了讓互斥鎖起作用,存取共享變數時需要加鎖,操作完成之後需要解鎖。相同的互斥鎖不允許多次加鎖,以免出現競態條件。
如果沒有接收方,發送者將會阻塞;相同地,如果沒有發送方,接收者將會阻塞。基於這個特性,所以我們不能將無緩衝的 channel 作為鎖定來使用。
我們來看看緩衝 channel 是否可以當做互斥鎖來使用。
緩衝大小為1 的channel 具有如下的特性:如果緩衝滿了,發送時將會阻塞;如果快取騰空,發送時就會解除阻塞。
顯然,這種channel 的阻塞特性是可取的,與互斥鎖的特性做個對比:
緩衝滿時<--> 上鎖
緩衝騰空<--> 解鎖
我們一起透過程式碼示範下這個特性。
我們假設有一列名字需要寫入到檔案中,每個名字都需要連續寫1000 次,且不允許不同名字出現交叉狀況。
package main import ( "errors" "fmt" "os" "sync" ) func main() { file, err := os.Create("record.txt") defer func() { if err := recover(); err != nil { fmt.Printf("Error encounter: %w", err) } file.Close() }() if err != nil { panic(errors.New("Cannot create/open file")) } ss := []string{ //string slice literals "James", "Avery", "Peter", "John", "Beau", } chanLock := make(chan int, 1) //1 var wg sync.WaitGroup for _, str := range ss { //2 wg.Add(1) //amended thanks to response from Wang //Sheng go func(aString string) { chanLock <- 1 //3 for i := 0; i < 1000; i++ { file.WriteString(aString + "\n") } <-chanLock //4 wg.Done() //5 }(str) //pass by value } wg.Wait() }
在上面的程式碼中,//1 我們建立了緩衝為 1 的 channel。 //2 我們建立了個數與名字數量相同的 goroutine。 //3 相當於加鎖,//4 相當於解鎖,這樣就實現了多 goroutine 之間同步地將名字寫入到 record.txt 文件,但每次只會有一個 goroutine 操作該文件。
要注意的是,我們透過 WaitGroup 來保證子 goroutine 完成任務之前,主協程不會退出。
希望這篇文章對你有幫助,enjoy coding!
以上是學到了!將緩衝 channel 當做 Mutex 來使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!