看了這篇有關Go語言的Channel文章,整個人都感覺不好了
Go的Channel是一個很強大的併發資料模型,在一個傳送者和多個消費者情況下工作得最好,但是如果是多個傳送者,那麼在Channel關閉時需要協調多個傳送者,等待它們傳送消費完畢,同時也會導致一個Channel多次關閉的情況,這個問題Go社群已經注意到,並正在試圖解決:issue#14601
讓我們看看來自
這裡狀態變數bestScore 並不使用互斥鎖進行保護,因為我們只有一個goroutine管理這個狀態,新的分數是透過一個channel接受的。
現在開始一個啟動game的構造器:
下一步,讓我們假設有人給我們一個Player介面,這個介面可以獲得分數score,也可能返回錯誤,因為也許網路連線錯誤或player已經退出。
下面是處理player,首先檢查錯誤,然後將接受到的分數發給channel。
好了,我們有一個Game型別,能夠以執行緒安全方式跟蹤Player的最高分。
當你將這個遊戲上線執行後,你獲得了非常地成功,你的遊戲伺服器上執行了很多這樣的Game程式。但是你會發現人們有時會離開你的遊戲,許多遊戲裡面沒有任何玩家,但是遊戲本身沒有停止而是不停地在迴圈執行,你被(*Game).run協程搞得不知所措了。
其實這個案例完全可以不使用Channel簡單地完成:
這裡只是引入了互斥鎖,替代了原來的Channel方式。使用傳統的同步鎖替代CSP的Go channel居然解決了問題。
那麼,讓我們看看Go語言的Channel背後是什麼?Channel是使用鎖保證連續訪問的執行緒安全性,使用Channel同步化記憶體的訪問,其實你是在使用鎖(banq注:這違背了CSP模型本身定義,CSP存在的前提就是避免使用鎖。),鎖被執行緒安全的佇列給包裝了,那麼Go的這個神秘的鎖與直接使用標準庫中mutex有什麼區別?下面是兩者效能測試結果:
對於unbuffered channel也是同樣測試結果。(測試結果表明:Channel效能比mutex效能要差得多)
也許Go的排程器會提高,但是這意味著老的舊的互斥鎖和條件變數表現得非常好,更有效,更快速。如果你需要效能,那麼就使用這些真正的方法(互斥鎖等方法)。
此後,該文作者還指出Channel其實並不能和其他併發原始模型很好地組合在一起。比如Channel和互斥鎖放在一起就帶來不確定性。
他還指出,在API中使用Channel反而使得問題變得糟糕,你完全可以在沒有任何協程情況下使用互斥鎖mutexe, semaphores,和回撥callbacks編寫API,有時,回撥函式反而更加強大,雖然,goroutine協程口口聲聲說是用來替代萬惡的回撥函式的。協程只是比執行緒較為輕量,但是較為輕量不代表它是最輕量的。
他還談到:對一個已經關閉的Channel再進行關閉或傳送操作是非常痛苦,如果你要關閉一個Channel,需要將關閉狀態進行同步化操作,使用mutex互斥鎖等,但是它們又不能很好地協調在一起,為什需要有關閉的狀態?因為這樣才會防止其他寫入者同時寫入或關閉一個已經關閉的channel。
最後,該文博主還未未來Go 2.0提出了更多建設性意見。
原文:
讓我們看看來自
go channels are bad and you should feel bad | jtol一文是如何抱怨Channel有多不好,以下是原文大意轉述:
首先,不用質疑的是,該文博主自從2010中期就開始使用Go語言,參與編寫了425K行的純Go程式碼,併為Go社群提供了不少開源專案。
Go語言是以Channel和Goroutine(輕量執行緒)著稱,它的理論模型主要是基於CSP模型(Communicating Sequential Processes),但是該文博主認為,從程式設計師角度看,Go的Channel模型實現方式是錯誤的,它是一個徹底的反模式。
CSP模型是一種高併發計算模型,這種模型本身是試圖透過一個通道同步管理傳送和接受,但是,如果一旦你引入其他同步機制如互斥鎖mutex、semaphore或條件判斷變數,你就再也不是純的CSP了。
下面讓我們看看使用GO編寫CSP模型,這個案例是關於如何接受到選手的最高分。
首先,使用Game作為資料結構:
type Game struct { bestScore int scores chan int } <p class="indent"> |
這裡狀態變數bestScore 並不使用互斥鎖進行保護,因為我們只有一個goroutine管理這個狀態,新的分數是透過一個channel接受的。
func (g *Game) run() { for score := range g.scores { if g.bestScore < score { g.bestScore = score } } } <p class="indent"> |
現在開始一個啟動game的構造器:
func NewGame() (g *Game) { g = &Game{ bestScore: 0, scores: make(chan int), } go g.run() return g } <p class="indent"> |
下一步,讓我們假設有人給我們一個Player介面,這個介面可以獲得分數score,也可能返回錯誤,因為也許網路連線錯誤或player已經退出。
type Player interface { NextScore() (score int, err error) } <p class="indent"> |
下面是處理player,首先檢查錯誤,然後將接受到的分數發給channel。
func (g *Game) HandlePlayer(p Player) error { for { score, err := p.NextScore() if err != nil { return err } g.scores <- score } } <p class="indent"> |
好了,我們有一個Game型別,能夠以執行緒安全方式跟蹤Player的最高分。
當你將這個遊戲上線執行後,你獲得了非常地成功,你的遊戲伺服器上執行了很多這樣的Game程式。但是你會發現人們有時會離開你的遊戲,許多遊戲裡面沒有任何玩家,但是遊戲本身沒有停止而是不停地在迴圈執行,你被(*Game).run協程搞得不知所措了。
其實這個案例完全可以不使用Channel簡單地完成:
type Game struct { mtx sync.Mutex bestScore int } func NewGame() *Game { return &Game{} } func (g *Game) HandlePlayer(p Player) error { for { score, err := p.NextScore() if err != nil { return err } g.mtx.Lock() if g.bestScore < score { g.bestScore = score } g.mtx.Unlock() } } <p class="indent"> |
這裡只是引入了互斥鎖,替代了原來的Channel方式。使用傳統的同步鎖替代CSP的Go channel居然解決了問題。
那麼,讓我們看看Go語言的Channel背後是什麼?Channel是使用鎖保證連續訪問的執行緒安全性,使用Channel同步化記憶體的訪問,其實你是在使用鎖(banq注:這違背了CSP模型本身定義,CSP存在的前提就是避免使用鎖。),鎖被執行緒安全的佇列給包裝了,那麼Go的這個神秘的鎖與直接使用標準庫中mutex有什麼區別?下面是兩者效能測試結果:
BenchmarkSimpleSet-8 3000000 391 ns/op BenchmarkSimpleChannelSet-8 1000000 1699 ns/op <p class="indent"> |
對於unbuffered channel也是同樣測試結果。(測試結果表明:Channel效能比mutex效能要差得多)
也許Go的排程器會提高,但是這意味著老的舊的互斥鎖和條件變數表現得非常好,更有效,更快速。如果你需要效能,那麼就使用這些真正的方法(互斥鎖等方法)。
此後,該文作者還指出Channel其實並不能和其他併發原始模型很好地組合在一起。比如Channel和互斥鎖放在一起就帶來不確定性。
他還指出,在API中使用Channel反而使得問題變得糟糕,你完全可以在沒有任何協程情況下使用互斥鎖mutexe, semaphores,和回撥callbacks編寫API,有時,回撥函式反而更加強大,雖然,goroutine協程口口聲聲說是用來替代萬惡的回撥函式的。協程只是比執行緒較為輕量,但是較為輕量不代表它是最輕量的。
他還談到:對一個已經關閉的Channel再進行關閉或傳送操作是非常痛苦,如果你要關閉一個Channel,需要將關閉狀態進行同步化操作,使用mutex互斥鎖等,但是它們又不能很好地協調在一起,為什需要有關閉的狀態?因為這樣才會防止其他寫入者同時寫入或關閉一個已經關閉的channel。
最後,該文博主還未未來Go 2.0提出了更多建設性意見。
原文:
go channels are bad and you should feel bad | jtol
[該貼被admin於2016-03-07 10:17修改過]
相關文章
- 為什麼每個人都討厭GO語言?Go
- 用 Go 語言 buffered channel 實作 Job QueueGo
- 今天看了婚紗攝影的一個工作室,感覺還不錯
- Go語言微服務系列文章Go微服務
- Scala: 感覺像動態的靜態語言
- go : channel , queue , 程式管理 , 關閉channel ?Go
- Go語言常用的運算子篇Go
- 如何看待這篇有關菠菜程式搭建員的文章有感而發
- 什麼是Go語言?Go語言有什麼特點?Go
- 有關連結串列的小技巧,我都給你總結好了
- 深度解密 Go 語言之 channel解密Go
- 深度解密Go語言之channel解密Go
- 如何入門GO語言?這份GO語言超詳細入門教程你值得擁有-千鋒Go
- 為什麼很多公司都轉型go語言開發?Go語言能做什麼Go
- Go--關於 goroutine、channelGo
- go語言有哪些優勢Go
- Go 語言關於 Type Assertions 的 坑Go
- go語言開發有哪些工具Go
- 關於C語言Switch語句,先學這些技巧夠不夠?C語言
- 如何優雅的關閉Go Channel「譯」Go
- 你為什麼不應該過度關注go語言的逃逸分析Go
- 我學 Go 語言這一年Go
- Go語言:未指定型別的常量(untyped int constant)和大整數的關係Go型別
- Go語言————1、初識GO語言Go
- 一個自己都感覺幼稚的猜數遊戲遊戲
- go與其他語言有什麼區別?學習go語言怎麼樣Go
- 有Go語言實戰培訓班嗎?go語言開發環境搭建Go開發環境
- Go channel 的妙用Go
- 看了這篇還不會Linux效能分析和優化,你來打我Linux優化
- 個人經驗分享如何閱讀Go語言原始碼Go原始碼
- 飛雪無情的部落格Go語言、Android相關的十大熱門文章GoAndroid
- 最新Go語言學習路線圖 帶你通關Go語言-千鋒Go
- 《Go 語言併發之道》讀後感 - 第二章Go
- 每個人都應該懂點函數語言程式設計函數程式設計
- 【面試篇】Go語言常見踩坑(一)面試Go
- [譯] Go 語言的整潔架構之道 —— 一個使用 gRPC 的 Go 專案整潔架構例子Go架構RPC
- 與LINQ有關的語言特性
- 字型,讓語言有了視覺形態視覺