亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何使變量線程安全

如何使變量線程安全

Go
至尊寶的傳說 2023-04-24 16:25:06
我是 Go 的新手,我需要使變量線程安全。我知道在 java 中你可以只使用關鍵字synchronized,但在 go 中似乎不存在這樣的東西。有什么方法可以同步變量嗎?
查看完整描述

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.RWMutexinstead 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)可以訂閱接收消息/更新,并且代理能夠向訂閱的客戶端廣播消息。在一個有大量客戶端隨時可能訂閱/取消訂閱的系統中,并且可能需要隨時廣播消息,以安全的方式同步所有這些將很復雜。明智地使用通道,這個代理實現相當干凈和簡單。該實現對于并發使用是完全安全的,支持“無限”客戶端,并且不使用單個互斥鎖或共享變量,僅使用通道。


查看完整回答
反對 回復 2023-04-24
  • 1 回答
  • 0 關注
  • 117 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號