1 回答

TA貢獻1155條經驗 獲得超0個贊
synchronized
在 Java 中是一種只允許單個線程執行代碼塊的方法(在任何給定時間)。
在 Go 中有許多結構可以實現這一點(例如互斥鎖、通道、等待組、原語sync/atomic
),但 Go 的格言是:“不要通過共享內存進行通信;相反,通過通信共享內存?!?/em>
因此,與其鎖定和共享變量,不如嘗試不這樣做,而是在 goroutine 之間傳遞結果,例如使用通道(這樣您就不必訪問共享內存)。
當然,在某些情況下,最簡單、直接的解決方案是使用互斥鎖來保護多個 goroutine 對變量的并發訪問。在這種情況下,您可以這樣做:
var (
? ? mu? ? ? ? sync.Mutex
? ? protectMe int
)
func getMe() int {
? ? mu.Lock()
? ? me := protectMe
? ? mu.Unlock()
? ? return me
}
func setMe(me int) {
? ? mu.Lock()
? ? protectMe = me
? ? mu.Unlock()
}
上述解決方案可以在幾個方面進行改進:
使用
sync.RWMutex
instead of?sync.Mutex
,以便getMe()
可以鎖定為只讀,這樣多個并發讀者就不會互相阻塞。在(成功)鎖定之后,建議使用 解鎖
defer
,這樣如果后續代碼中發生錯誤(例如運行時恐慌),互斥體仍將被解鎖,避免資源泄漏和死鎖。盡管此示例非常簡單,但不會發生任何不良情況,也不能保證無條件地使用延遲解鎖。最好讓互斥體靠近它應該保護的數據。因此,“包裝”
protectMe
及其mu
在結構中是個好主意。而且如果我們這樣做,我們也可以使用嵌入,因此鎖定/解鎖變得更加方便(除非必須不公開此功能)。
因此,上述示例的改進版本可能如下所示(在Go Playground上嘗試):
type Me struct {
? ? sync.RWMutex
? ? me int
}
func (m *Me) Get() int {
? ? m.RLock()
? ? defer m.RUnlock()
? ? return m.me
}
func (m *Me) Set(me int) {
? ? m.Lock()
? ? m.me = me
? ? m.Unlock()
}
var me = &Me{}
func main() {
? ? me.Set(2)
? ? fmt.Println(me.Get())
}
此解決方案還有另一個優點:如果您需要 的多個值Me,它會自動為每個值具有不同的、單獨的互斥鎖(我們的初始解決方案需要為每個新值手動創建單獨的互斥鎖)。
雖然這個例子是正確有效的,但可能不實用。因為保護單個整數并不真正需要互斥體。我們可以使用sync/atomic包實現相同的目的:
var protectMe int32
func getMe() int32 {
? ? return atomic.LoadInt32(&protectMe)
}
func setMe(me int32) {
? ? atomic.StoreInt32(&protectMe, me)
}
這個解決方案更短、更干凈、更快。如果您的目標只是保護單個值,則首選此解決方案。如果您應該保護的數據結構更復雜,atomic
甚至可能不可行,那么使用互斥量可能是合理的。
現在在展示了共享/保護變量的例子之后,我們還應該給出一個例子,我們應該實現什么目標來實現“不要通過共享內存進行通信;相反,通過通信共享內存”。
情況是你有多個并發的 goroutines,你使用一個變量來存儲一些狀態。一個 goroutine 更改(設置)狀態,另一個 goroutine 讀?。ǐ@取)狀態。要從多個 goroutine 訪問此狀態,必須同步訪問。
這個想法是不要有這樣的“共享”變量,而是一個 goroutine 設置的狀態,它應該“發送”它,而另一個 goroutine 會讀取它,它應該是狀態“發送到”(或者換句話說,另一個 goroutine 應該接收更改后的狀態)。所以沒有共享狀態變量,取而代之的是2 個 goroutines 之間的通信。Go 為這種“協程間”通信提供了出色的支持:channels。對通道的支持內置于語言中,有send statements,receive operators和其他支持(例如你可以循環在通道上發送的值)。
讓我們看一個實際/現實生活中的例子:“經紀人”。代理是一個實體,其中“客戶端”(goroutines)可以訂閱接收消息/更新,并且代理能夠向訂閱的客戶端廣播消息。在一個有大量客戶端隨時可能訂閱/取消訂閱的系統中,并且可能需要隨時廣播消息,以安全的方式同步所有這些將很復雜。明智地使用通道,這個代理實現相當干凈和簡單。該實現對于并發使用是完全安全的,支持“無限”客戶端,并且不使用單個互斥鎖或共享變量,僅使用通道。
- 1 回答
- 0 關注
- 117 瀏覽
添加回答
舉報