Go 語言的緩衝通道:提示和技巧

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。Mahadevan Ramachandran • January 15通道和 goroutine 是 Go 語言基於 CSP( communicating sequential processes ,通訊順序進程)並發機制的核心部分。閱讀本文可以學到一些關於channel的提示和技巧,尤其是“緩衝” channel ,在 “生產者-消費者” 情境中廣泛使用了緩衝通道作為隊列。## 緩衝通道 = 隊列緩衝通道是固定容量的先進先出(FIFO)隊列。容量在隊列建立的時候就已經固定——其大小不能在運行時更改。```goqueue := make(chan Item, 10) // queue 的容量是 10```queue 中的一個元素最大可高達 64 KiB , 而且 queue 可以儲存指標和非指標元素。如果你堅持要用指標,抑或元素本身就是指標類型,那就需要由你自己來保證在 queue 中被使用的元素所指向的對象是有效。```goqueue := make(chan *Item, 10)item1 := &Item{Foo: "bar"}queue <- item1item1.Foo = "baz" // 有效,但這不是好習慣!```生產者(將元素放入隊列的代碼)在把元素入隊時可以選擇是否阻塞。```go// queue 滿後會發生阻塞queue <- item// 為了能不阻塞的放入元素, 代碼如下:var ok boolselect { case queue <- item: ok = true default: ok = false}// 在這裡, "ok" is:// true => 不阻塞的將元素入隊// false => 元素沒有入隊, 會因為queue已滿而阻塞```消費者通常從隊列中取出元素並處理它們。如果隊列為空白並且消費者無事可做,就會發生阻塞,直到生產者放入一個元素。```go// 取出一個元素, 或者一直等待,直到可以取出元素item := <- queue```如果不希望消費者等待,代碼如下:```govar ok boolselect { case item = <- queue: ok = true default: ok = false}// 在這裡, "ok" is:// true => 從queue中取出元素item (或者queue已經關閉,見下)// false => 沒有取出元素, queue為空白而發生阻塞```## 關閉緩衝通道緩衝通道最好是由生產者關閉,通道關閉事件會被發送給消費者。如果你需要在生產者或者消費者之外關閉通道,那麼你必須使用外部同步來確保生產者不會試圖向已關閉的通道寫入(這會引發一個 panic )。```goclose(queue) // 關閉隊列close(queue) // "panic: 關閉一個已關閉的通道"```## 讀取或者寫入關閉的通道你能向已關閉的通道寫入嗎?當然不能。```goqueue <- item // "panic: 向已關閉的通道寫入"```那麼能從已關閉的通道讀取嗎?事實上,在往下翻之前,請先猜測下這段代碼的輸出結果:```gopackage mainimport "fmt"func main() { queue := make(chan int, 10) queue <- 10 queue <- 20 close(queue) fmt.Println(<-queue) fmt.Println(<-queue) fmt.Println(<-queue)}```這裡有運行以上代碼的 [連結](https://play.golang.org/p/ot87ro27tFk) .(譯者註:以下是運行結果)```10200```吃驚吧?如果你猜錯了,記住你要先看這裡!:-)在已關閉的通道上的讀取行為比較特殊:- 如果還有元素沒有被取出,那麼讀取操作會照常進行。- 如果隊列已空並且已被關閉,讀取不會阻塞。- 在為空白並且已經關閉的通道上讀取時會返回通道中元素類型的 “零值”。這些能讓你明白為什麼上面的程式會列印出這種結果。但是你又怎麼區分讀取到的資料是否有效呢?畢竟,“零值”也可能是有效值。答案在下面:```goitem, valid := <- queue// 在這裡, "valid" 取值:// true => "item" 有效// false => "queue" 已經關閉, "item" 只是一個 “零值”```因此你在寫消費者代碼的時候可以這樣:```gofor { item, valid := <- queue if !valid { break } // 處理 item}// 到這裡,所有被放入到 queue 中的元素都已經處理完畢,// 並且 queue 也已經關閉```其實,“for..range” 迴圈是一種更加簡單的寫法:```gofor item := range queue { // 處理 item}// 到這裡,所有被放入到 queue 中的元素都已經處理完畢,// 並且 queue 也已經關閉```最後,我們可以把非阻塞和檢查元素有效性結合到一起:```govar ok, valid boolselect { case item, valid = <- queue: ok = true default: ok = false}// 到這裡:// ok && valid => item 有效, 可以使用// !ok => 通道沒有關閉,但是通道為空白,稍後重試// ok && !valid => 通道已經關閉,退出輪詢```## 諮詢和訓練需要協助獲得一個使用 Golang 的項目?我們在建立和運行生產級 Go 平台軟體解決方案領域擁有豐富經驗。我們可以協助你架構和設計 Go 平台項目,或者為使用 Go 工作的團隊提供建議和監督。我們也會為希望開展Go項目的團隊提供培訓或者提升 Golang 知識。[這裡發現更多](https://www.rapidloop.com/training) 或者 [馬上聯絡我們](https://www.rapidloop.com/contact) 來討論你的需求!**Mahadevan Ramachandran**Co-founder & CEO, RapidLoop [@mdevanr](https://twitter.com/mdevanr)

via: https://www.rapidloop.com/blog/golang-channels-tips-tricks.html

作者:Aaron Schlesinger 譯者:sunzhaohao 校對:polaris1119

本文由 GCTT 原創編譯,Go語言中文網 榮譽推出

本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽

739 次點擊  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.