Golang 有趣的 channel 應用

2021-09-08 03:42:41 字數 2565 閱讀 3120

嚴格意義上說,本文是我另外一片文章《golang funny: play with channel》的中文版本。不過,畢竟是用中文當母語的,所以就不翻譯了,重新按照那個內容寫過吧。

channel 是 golang 裡相當有趣的乙個功能,在我使用 golang 編碼的經驗裡,大部分事件都會是在享受 channel 和 goroutine 配合的樂趣。所以本文主要介紹 channel 的一些有趣的用法。

通道提供了一種機制,它在兩個併發執行的函式之間進行同步,並通過傳遞(與該通道元素型別相符的)值來進行通訊。

這個個描述又乏味、又枯燥。在我第一次閱讀的時候,完全不明白這到底是個什麼玩意。事實上,可以認為 channel 是乙個管道或者先進先出佇列,非常簡單且輕量。channel 並不是 golang 首創的。它同樣作為內建功能出現在其他語言中。在大多數情況下,它是乙個又大、又笨、又複雜的訊息佇列系統的乙個功能。

下面就來一起找點樂子吧!

生產者產生一些資料將其放入 channel;然後消費者按照順序,乙個乙個的從 channel 中取出這些資料進行處理。這是最常見的 channel 的使用方式。當 channel 的緩衝用盡時,生產者必須等待(阻塞)。換句話說,若是 channel 中沒有資料,消費者就必須等待了。

生產者

func producer(c chan int64, max int) 

}

生產者生成「max」個 int64 的數字,並且將其放入 channel 「c」 中。需要注意的是,這裡用 defer 在函式推出的時候關閉了 channel。

消費者

func consumer(c chan int64) 

}}

從 channel 中乙個乙個的讀取 int64 的數字,然後將其列印在螢幕上。當 channel 被關閉後,變數「ok」將被設定為「false」。

當生讓產者可以順序的生成整數。它就是乙個自增長 id 生成器。我將這個功能封裝成了乙個包。並將其**託管在這裡。使用示例可以參考這裡的**。

type autoinc struct 

func new(start, step int) (ai *autoinc)

go ai.process()

return

}func (ai *autoinc) process() ()

for i := ai.start; ai.running ; i=i+ai.step

}func (ai *autoinc) id() int

func (ai *autoinc) close()

我在 gearman 服務的 api 包 gearman-go 中使用了訊號量。在 worker/worker.go 的 232 行,在並行的 worker.exec 的數量達到 worker.limit 時,將被阻塞。

var sem = make(chan int, maxoutstanding)

func handle(r *request)

func serve(queue chan *request)

}

當然可以修改自增長 id 生成器。讓生產者生成隨機數放入 channel。不過這挺無聊的,不是嗎?

func producer(c chan int64, max int) 

}}

當乙個 channel 被 read/write 阻塞時,它會被永遠阻塞下去,直到 channel 被關閉,這時會產生乙個 panic。channel 沒有內建用於超時的定時器。並且似乎也沒有計畫向 channel 新增乙個這樣的功能。但在大多數情況下,我們需要乙個超時機制。例如,由於生產者執行的時候發生了錯誤,所以沒有向 channel 放入資料。消費者會被阻塞到 channel 被關閉。每次出錯都關閉 channel?這絕對不是乙個好主意。

這裡有乙個解決方案:

c := make(chan int64, 5)

defer close(c)

timeout := make(chan bool)

defer close(timeout)

go func() ()

select

你注意到 select 語句了嗎?哪個 channel 先有資料,哪個分支先執行。因此……還需要更多的解釋嗎?

這同樣被使用在gearman-go 的客戶端 api 實現中,第 238 行。

在本文的英文版本發布後,@mjq 提醒我說可以用 time.after。在專案中,這確實是更好的寫法。我得向他道謝!同時我也閱讀了 src/pkg/time/sleep.go 第 74 行,time.after 的實現。其內部實現與上面的**完全一致。

上面提到的各種有趣的應用當然也可以在其他訊息佇列中實現,不過由於 channel 的簡單和輕量,使得 golang 的 channel 來實現這些有趣的功能具有實際意義,並有真實的應用場景。其實,我覺得有趣的 channel 用法遠不止這些。如果你發現了其他有趣的玩法,請務必告訴我。謝謝啦!

golang中併發sync和channel

golang中實現併發非常簡單,只需在需要併發的函式前面新增關鍵字 go 但是如何處理go併發機制中不同goroutine之間的同步與通訊,golang 中提供了sync包和channel機制來解決這一問題 sync 包提供了互斥鎖這類的基本的同步原語.除 once 和 waitgroup 之外的型...

golang中併發sync和channel

golang中實現併發非常簡單,只需在需要併發的函式前面新增關鍵字 go 但是如何處理go併發機制中不同goroutine之間的同步與通訊,golang 中提供了sync包和channel機制來解決這一問題 sync 包提供了互斥鎖這類的基本的同步原語.除 once 和 waitgroup 之外的型...

golang中併發sync和channel

golang中實現併發非常簡單,只需在需要併發的函式前面新增關鍵字 go 但是如何處理go併發機制中不同goroutine之間的同步與通訊,golang 中提供了sync包和channel機制來解決這一問題 sync 包提供了互斥鎖這類的基本的同步原語.除 once 和 waitgroup 之外的型...