![image](https://raw.githubusercontent.com/studygolang/gctt-images/master/sync-goroutine/part1.jpeg)假設 Go 程式啟動了兩個 goroutine:```gopackage mainimport ( "fmt" "sync")func main() { var v int var wg sync.WaitGroup wg.Add(2) go func() { v = 1 wg.Done() }() go func() { fmt.Println(v) wg.Done() }() wg.Wait()}```兩個 goroutine 都對共用變數 *v* 進行操作。其中一個賦新值(寫操作)而另一個列印變數的值(讀操作)。> *sync 包中的 [WaitGroup](https://golang.org/pkg/sync/#WaitGroup) 被用來等待兩個非 main 的 goroutine 結束。否則,我們甚至都不能確保其中任意一個 goroutine 有被啟動。*由於不同的 goroutine 是相互獨立的任務,它們進行的操作之間沒有任何隱含的順序。在上面的例子中,我們不清楚會列印出 `0` 還是 `1`。如果在 `fmt.Println` 被觸發時,另一個 goroutine 已經執行了指派陳述式 `v = 1`,那麼輸出會是 `1`。然而,在程式真正被執行之前一切都是未知的。換句話說,指派陳述式和調用 `fmt.Println` 是無序的 —— 它們是並發的。如果我們無法通過查看源碼斷定程式的行為,這是不好的。Go 的規範引入了記憶體操作(讀和寫)的偏序(partial order)關係(*先行發生原則* *happen before*)。這個順序使我們能夠推斷程式的行為。另外,這門語言中的一些機制允許程式員強制實行操作的順序。在單個 goroutine 中,所有操作的順序都與它們在源碼中的位置一致。```gowg.Add(2)wg.Wait()```上面例子中的函數調用是有序的,因為它們在同一個 goroutine 中 —— `wg.Add(2)` 先於 `wg.Wait()` 被執行。## 1. 通道(Channel)使用 channel 進行通訊是最重要的同步方法。往 channel 中發送資料發生在接收資料之前:```govar v intvar wg sync.WaitGroupwg.Add(2)ch := make(chan int)go func() { v = 1 ch <- 1 wg.Done()}()go func() { <-ch fmt.Println(v) wg.Done()}()wg.Wait()```新的東西是 *ch* 這個 channel。由於接收資料發生在往 channel 中發送資料之後,而發送資料發生在給 *v* 賦值之後,所以上述程式的輸出永遠是 `1`。給 *v* 賦值 → 發送到 *ch* → 從 *ch* 接收 → 列印 *v*第一個箭頭和第三個箭頭都是由同一個 goroutine 中的順序確定的。使用 channel 進行通訊帶來了第二個箭頭。最終,分散在兩個 goroutine 中的操作是有序的。## 2. sync 包[sync](https://golang.org/pkg/sync/) 包提供了同步的原語。其中能解決我們的問題的是 [Mutex](https://golang.org/pkg/sync/#Mutex)。*sync.Mutex* 類型的變數 *lock* 保證第二次調用 `lock.Lock()` 發生在第一次調用 `lock.Unlock()` 之後。第三次調用 `lock.Lock()` 發生在第二次調用 `lock.Unlock()` 之後。一般來說,如果 *m* < *n*,那麼第 *n* 次調用 `lock.Lock()` 發生在第 *m* 次調用 `lock.Unlock()` 之後。讓我們來看看在我們的同步問題中如何利用這個知識:```govar v intvar wg sync.WaitGroupwg.Add(2)var m sync.Mutexm.Lock()go func() { v = 1 m.Unlock() wg.Done()}()go func() { m.Lock() fmt.Println(v) wg.Done()}()wg.Wait()```---在後續的文章中將會展示更多關於使用 channel 進行通訊的內容(比如怎樣使用帶緩衝的 channel),[sync](https://golang.org/pkg/sync/) 包都提供了什麼內容也會詳細解釋。點贊以協助別人發現這篇文章。如果你想得到新文章的更新,請關注我。## 資源- [Go 的記憶體模型 —— Go 程式設計語言](https://golang.org/ref/mem)>The Go memory model specifies the conditions under which reads of a variable in one goroutine can be guaranteed to…<br>*golang.org*- [像個 Gopher 一樣使用 *panic*](https://medium.com/golangspec/panicking-like-a-gopher-367a9ce04bb8)> Errors while executing program in Go (after successful compilation and when OS process has been started) take the form…<br>*medium.com**[保留部分著作權](http://creativecommons.org/licenses/by/4.0/)**[Golang](https://medium.com/tag/golang?source=post)**[Programming](https://medium.com/tag/programming?source=post)**[Concurrency](https://medium.com/tag/concurrency?source=post)**[Synchronization](https://medium.com/tag/synchronization?source=post)**[Goroutines](https://medium.com/tag/goroutines?source=post)***喜歡讀嗎?給 Michał Łowicki 一些掌聲吧。**簡單鼓勵下還是大喝采,根據你對這篇文章的喜歡程度鼓掌吧。
via: https://medium.com/golangspec/synchronized-goroutines-part-i-4fbcdd64a4ec
作者:Michał Łowicki 譯者:krystollia 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
142 次點擊