国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

使用Go語言時,謹防鎖拷貝!

開發 后端
相信大家對 Go 語言的鎖拷貝問題并不陌生,那我們應該如何規范使用Go 語言才能規避這個問題呢?一起來看作者是如何處理的。

[[413679]]

本文轉載自微信公眾號「Golang來啦」,作者Seekload。轉載本文請聯系Golang來啦公眾號。

四哥水平有限,如有翻譯或理解錯誤,煩請幫忙指出,感謝!

相信大家對 Go 語言的鎖拷貝問題并不陌生,那我們應該如何規范使用Go 語言才能規避這個問題呢?一起來看作者是如何處理的。

原文如下:

假設我們有一個包含 map 的結構體,現在想在方法中修改這個 map,看下面的例子[1]:

  1. package main 
  2.  
  3. import "fmt" 
  4.  
  5. type Container struct { 
  6.   counters map[string]int 
  7.  
  8. func (c Container) inc(name string) { 
  9.   c.counters[name]++ 
  10.  
  11. func main() { 
  12.   c := Container{counters: map[string]int{"a": 0, "b": 0}} 
  13.  
  14.   doIncrement := func(name string, n int) { 
  15.     for i := 0; i < n; i++ { 
  16.       c.inc(name
  17.     } 
  18.   } 
  19.  
  20.   doIncrement("a", 100000) 
  21.  
  22.   fmt.Println(c.counters) 

Container 包含一個計數器集合,按 name 區分。inc() 會按 name 對相應的計數器執行自增操作(假設計數器存在)。main() 里循環多次調用 inc()。

執行上面的代碼,輸出:

  1. map[a:100000 b:0] 

現在假設有兩個 goroutine 會并發地調用 inc()。因為我們必須小心競爭條件,所以使用了 Mutex 保護臨界區。

  1. package main 
  2.  
  3. import ( 
  4.   "fmt" 
  5.   "sync" 
  6.   "time" 
  7.  
  8. type Container struct { 
  9.   sync.Mutex                       // <-- Added a mutex 
  10.   counters map[string]int 
  11.  
  12. func (c Container) inc(name string) { 
  13.   c.Lock()                         // <-- Added locking of the mutex 
  14.   defer c.Unlock() 
  15.   c.counters[name]++ 
  16.  
  17. func main() { 
  18.   c := Container{counters: map[string]int{"a": 0, "b": 0}} 
  19.  
  20.   doIncrement := func(name string, n int) { 
  21.     for i := 0; i < n; i++ { 
  22.       c.inc(name
  23.     } 
  24.   } 
  25.  
  26.   go doIncrement("a", 100000) 
  27.   go doIncrement("a", 100000) 
  28.  
  29.   // Wait a bit for the goroutines to finish 
  30.   time.Sleep(300 * time.Millisecond) 
  31.   fmt.Println(c.counters) 

你期望上面這段代碼會輸出什么呢?我得到的結果是這樣的:

  1. func (c *Container) inc(name string) { 
  2.   c.Lock() 
  3.   defer c.Unlock() 
  4.   c.counters[name]++ 

我們使用 mutex 時已經很小心了,怎么還會出問題呢?你覺得應該如何修復這個問題?提示:只需要改動一個字符的代碼就可以了!

代碼的問題在于,無論何時調用 inc(),c 都會是一份拷貝,因為 inc() 是定義在 Container 上,而非 *Container;換句話說,c 是值接受者,而不是指針接受者。因此,inc() 并不能真正修改 c 的內容。

但等等,文章第一個示例是如何工作的?在單協程的例子中,c 也是按值傳遞,但是為什么能得到正確的結果 -- 在 inc() 在對 map 所做的修改,能影響到 main() 函數的原始值。這是因為 map 是引用類型而非值類型。Container 里保存的是指向 map 的指針,而不是 map 實際的數據。所以即使我們創建 Container 的副本,counters 保存的仍是指向 map 的地址。

所以文章第一個例子也是存在問題的,盡管執行結果沒有問題,但是使用方法不符合官方指南[2] - 在方法中對原始數據進行修改,則方法應定義成指針方法,而非值方法。這里對 map 的使用給了我們一種錯誤的提示。作為練習,可以將第一個示例中的 map 換成 int 類型的計數器,并注意觀察 inc() 的副本是如何遞增的,在 inc() 中對副本做的修改不會影響到 main() 中的原始值。

Mutex 是值類型(可以看 Go 文檔[3]相關的定義,包括注釋里也明確地提示不能拷貝),復制再使用是錯誤的。復制僅僅是創建了一個新的 mutex,很顯然地,對計數器的互斥使用就失效了。

所以應該這樣修改,定義 inc() 方法時在 Container 之前添加 *:

  1. func (c *Container) inc(name string) { 
  2.   c.Lock() 
  3.   defer c.Unlock() 
  4.   c.counters[name]++ 

c 通過指針方式傳到方法中,指向的 Container 與 main() 函數里面的是同一個。

這個問題并不罕見,事實上,使用 go vet 命令就會發現這個問題:

  1. $ go tool vet method-mutex-value-receiver.go 
  2. method-mutex-value-receiver.go:19: inc passes lock by value: main.Container 

在我看來,實際上這個問題幫助我們理清了值接收者與指針接收者之間的區別。為了說明這一點,下面還有一個示例,這個示例與上面兩個示例沒有關系。這個示例使用到了 & 取值符和 %p 格式化輸出變量的地址。

  1. package main 
  2.  
  3. import "fmt" 
  4.  
  5. type Container struct { 
  6.   i int 
  7.   s string 
  8.  
  9. func (c Container) byValMethod() { 
  10.   fmt.Printf("byValMethod got &c=%p, &(c.s)=%p\n", &c, &(c.s)) 
  11.  
  12. func (c *Container) byPtrMethod() { 
  13.   fmt.Printf("byPtrMethod got &c=%p, &(c.s)=%p\n", c, &(c.s)) 
  14.  
  15. func main() { 
  16.   var c Container 
  17.   fmt.Printf("in main &c=%p, &(c.s)=%p\n", &c, &(c.s)) 
  18.  
  19.   c.byValMethod() 
  20.   c.byPtrMethod() 

執行代碼后輸出(如果在你的機器上執行,輸出的地址可能不同,但是這不影響說明問題):

  1. in main &c=0xc00000a060, &(c.s)=0xc00000a068 
  2. byValMethod got &c=0xc00000a080, &(c.s)=0xc00000a088 
  3. byPtrMethod got &c=0xc00000a060, &(c.s)=0xc00000a068 

main() 函數里創建了 Container 變量 c,并且輸出它的地址和它的成員 s 的地址,接著調用了 Container 的兩個方法。byValMethod() 是值接受者,因為是原值的拷貝所有打印的地址不一樣。另一方面,byPtrMethod() 是指針接收者,輸出的地址與 main() 函數輸出的地址一致,因為調用時獲取的是 c 實際的地址,而不是副本。

參考資料

[1]例子: https://github.com/eliben/code-for-blog/tree/master/2018/go-copying-mutex

[2]官方指南: https://golang.org/doc/faq#methods_on_values_or_pointers

[3]Go 文檔: https://golang.org/src/sync/mutex.go

 

責任編輯:武曉燕 來源: Golang來啦
相關推薦

2021-06-08 07:45:44

Go語言優化

2021-07-08 23:53:44

Go語言拷貝

2021-08-02 22:31:24

Go語言Append

2021-01-08 06:15:09

深拷貝淺拷貝寫時拷貝

2022-06-05 23:30:25

AES加密算法

2024-10-28 00:40:49

Go語法版本

2018-03-12 22:13:46

GO語言編程軟件

2024-01-02 10:38:22

Go語言數組

2024-04-01 00:02:56

Go語言代碼

2022-07-03 23:07:48

Go語言參數

2023-02-13 00:24:37

Go語言日志庫

2022-04-13 08:20:32

DockerGo項目

2020-08-12 08:56:30

代碼凱撒密碼函數

2024-02-06 17:57:06

Go語言任務

2022-07-04 14:41:31

Go 語言變長參數變長參數函數

2021-12-06 10:22:47

切片拷貝Python

2024-05-10 08:36:40

Go語言對象

2023-03-06 08:01:25

structGo語言

2023-01-16 00:12:20

Go語言Web

2023-07-03 00:44:26

Go語言MySQL
點贊
收藏

51CTO技術棧公眾號

99久久久精品视频| 欧美一级精品大片| 欧美高清第一页| 麻豆视频在线| 国产精品福利av| 这里只有精品66| 国产99久久| 亚洲视频在线观看视频| 男人av在线| 亚洲欧美综合网| 可以在线看黄的网站| 亚洲激情偷拍| 国产精品亚洲自拍| 蜜桃精品视频| 国产剧情在线观看一区二区| 国产精品视频999| 亚洲一区不卡在线| 成人在线观看免费播放| 亚洲欧洲一区二区三区久久| 天天色图综合网| 色播五月综合网| 亚洲一区二区三区精品在线| 亚洲精品成人自拍| 久久久久久美女精品| 久久免费国产视频| 欧美成人一二区| 日韩高清免费在线| av片在线观看网站| 欧美日韩一区二区电影| 三级视频网站在线| 亚洲韩国一区二区三区| 国产精品久久久久永久免费看| 高清成人在线观看| 免费观看国产视频在线| 老鸭窝亚洲一区二区三区| 91九色蝌蚪嫩草| 91麻豆精品国产91久久久平台 | 色综合久久久久综合体| 男女羞羞视频网站| 色婷婷色综合| 亚洲欧洲制服丝袜| 一区二区精品国产| 欧美日韩亚洲一区二区三区在线| 国产精品99久久久久久人| 青青草原在线亚洲| 国产成人在线精品| 中文字幕日韩欧美精品高清在线| 国产高清精品一区二区| 伊人久久综合| 日本一区二区三区www| 日韩精品高清不卡| 国产树林野战在线播放| 成人黄色777网| 亚洲精品无码久久久久久| 深夜福利一区二区三区| 欧美激情高清视频| 青青一区二区| 国产视频观看一区| 雨宫琴音一区二区在线| 久久综合色一本| 激情久久久久久久久久久久久久久久| 国产91视频一区| 久久综合精品国产一区二区三区| 国产熟人av一二三区| 亚洲视频一区二区免费在线观看| 全部a∨一极品视觉盛宴| 亚洲国产一区二区在线播放| 欧美色图另类| 日韩欧美成人激情| 色偷偷色偷偷色偷偷在线视频| 伊人精品在线观看| 男人天堂免费视频| 一本一道久久a久久精品综合| 国产ts人妖一区二区| 综合久久综合久久| 在线播放av更多| 日韩精品一区二区三区三区免费 | 国产午夜精品视频| 在线观看一级片| 欧美在线日韩在线| 夜夜操天天操亚洲| 亚洲国产日韩综合一区| 国产成人在线免费观看| 亚洲视频第二页| 欧美午夜宅男影院| 久久免费视频2| 久久日一线二线三线suv| 国产女王在线**视频| 欧美电影免费观看完整版| 日韩一区二区三免费高清在线观看| 午夜精品久久久久久久99热| 午夜久久福利| 国产免费一区二区视频| 亚洲欧美偷拍三级| av在线私库| 992tv在线成人免费观看| 日韩视频精品在线观看| 国产午夜福利视频在线观看| 色综合天天综合网天天狠天天| 欧美精品videosex| 中文字幕一区二区精品| 欧美美女一区| 做爰高潮hd色即是空| 欧美日韩激情视频8区| 日日夜夜一区| 欧美日韩成人一区二区三区 | 日韩精品视频中文字幕| 国产精品成人一区二区三区| 久久精品一区蜜桃臀影院| 成人免费观看视频大全| 欧美一级片久久久久久久| 日韩av电影一区| 嫩草影院永久入口| 精品视频在线观看日韩| 欧美日韩亚洲三区| 肥女人的一级毛片| 亚洲欧美一区二区三区久久| 永久91嫩草亚洲精品人人| 97超碰在线人人| 欧美一区二区免费| 日韩免费高清| 日韩色妇久久av| 欧美日韩国产精品一区二区不卡中文 | 97色伦图片97色伦在线电影| 国产视频在线观看一区二区| 综合久久精品| 高清国语自产在线观看| 精品国产一区久久久| 久久久久99| 美女做暖暖视频免费在线观看全部网址91 | 真不卡电影网| 欧美极品少妇xxxxx| 成人av网站在线| 国内精品伊人| 欧美亚洲另类色图| 久久精品国产亚洲7777| av男人天堂一区| 中文无码日韩欧| 91国产在线精品| 国产精品久久一级| 欧美成人午夜77777| 男女小视频在线观看| 91久久国产精品91久久性色| 欧美影片第一页| 久久这里只有| 欧美gay视频| 国产精品大陆在线观看| 美女爽到呻吟久久久久| 欧洲一区二区三区| 国产一区二区三区播放| 亚洲一级免费视频| 日本一区二区三区高清不卡 | 亚洲色诱最新| xxx.xxx欧美| 久久精品免费一区二区| 国产成+人+综合+亚洲欧洲| 日韩欧美福利视频| 三级成人在线视频| 欧美91在线|欧美| 米奇在线777| 精品国产一区二区三区免费| 91在线视频观看| 最近中文字幕mv免费高清在线| 91精品在线看| 欧美成人a在线| 2021久久国产精品不只是精品| 狼人精品一区二区三区在线 | 国产片一区二区三区| 91偷拍一区二区三区精品| 欧美精品日韩少妇| 免费在线看黄色片| 日本久久91av| 欧美一区二区视频观看视频| 国产麻豆精品在线| 欧美在线关看| av大全在线| 亚欧在线免费观看| 国产精品夜夜夜一区二区三区尤| 亚洲欧美日韩一区在线| 一区二区三区在线观看国产| 日韩成人一级片| 午夜精品影视国产一区在线麻豆| 国产区在线观看| 不卡的av中文字幕| 久久精品日产第一区二区三区 | 亚洲欧洲另类国产综合| 激情久久久久| 国产一区二区三区| aaa日本高清在线播放免费观看| 4444在线观看| 成人免费看黄网站| 亚洲午夜精品视频| 日韩欧美黄色动漫| www久久精品| 桃色av一区二区| 精品一二三四| 免费一级特黄毛片| 国产日韩欧美一区二区三区四区| 欧美精品video| 亚洲人成电影在线观看天堂色|