Channel:Go語言建立併發通訊的橋樑

閱讀時間約 14 分鐘


raw-image

👨‍💻簡介

昨天講到Goroutine有稍微簡單介紹Channel,Channel是Go語言中極為重要的併發通訊機制,它就像是不同goroutines之間的話筒,允許它們安全地傳遞資料和信息。這個強大的工具使得Go語言在處理併發任務時非常優雅和高效。通過Channel,可以協調goroutines的操作,防止競爭條件,並實現高效的併發程式設計。

什麼是Channel?

Channel是Go語言中一個強大的併發原語言功能,用於在不同的goroutine之間傳遞資料。它們提供了一種通訊的機制,可以讓goroutines之間安全地交換信息,而不需要額外的互斥鎖或信號量。

可以把channel比喻成傳紙條,傳紙條可以是單向,也可以是雙向,單向可以想像類似以前傳紙條的中間人,他只接收和傳遞,紙條也有大小的限制,寫滿了就不能再繼續寫,並且需要等待對方將紙條進行回覆後你才能繼續。

建立和使用Channel

在Go中,可以使用內建的make函數來建立一個新的Channel。以下是使用 make函數建立整數類型的Channel

ch := make(chan int)

建立完後,就可以將資料發送到Channel中,然後在其他goroutine中接收它們。以下是一個簡單的發送和接收資料的例子:

func main() {
ch := make(chan int)
go func() {
ch <- 42 // 發送資料到Channel
}()
value := <-ch // 從Channel接收資料
fmt.Println(value) // 輸出:42

這個例子中,我們建立了一個goroutine,它將數字42發送到Channel,然後主goroutine,也就是main函數,接收到這個數字。

關閉Channel

紙條用完會丟掉,Channel用完也是要關掉,您可能需要關閉Channel,以通知接收方不再有新的資料可用。要關閉Channel,可以使用close函數,如下所示:

close(ch)

接收方可以使用特殊的語法來檢查Channel是否已關閉:

value, ok := <-ch
if !ok {
// Channel已經關閉
}

這可以防止接收方在Channel已關閉的情況下繼續等待。 並且已經關閉的Channel依然可以進行接收的操作,但不能再進行發送。

package main

import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go func() {
for i := 0; i < 5; i++ {
time.Sleep(1 * time.Second)
ch <- i // 向Channel發送資料
}
close(ch) // 關閉Channel
}()
// 接收資料直到Channel被關閉
for val := range ch {
fmt.Println(val)
}
}

範例中首先創建了一個整數型的Channel ch,並在一個獨立的goroutine中向該Channel發送一系列數字。一旦所有資料都被發送完畢,我們通過close(ch)關閉了Channel。

主goroutine使用for num := range ch的方式來從Channel中接收資料,這是一種常見的接收方式。當Channel被關閉後,range迴圈會自動退出,從而避免了無限等待。

Channel的方向

傳紙條有方向,Channel肯定也有方向,Channel的方向主要用來限制操作方向,增加程式的安全性。Channel可以是雙向的(可以用來發送和接收),也可以是單向的(只能用來發送或接收)。

1. 雙向Channel(Bidirectional Channels)

  • 雙向Channel是最常見的類型,它允許進行發送和接收操作。
  • 建立方式:ch := make(chan T),其中T是所需的資料類型。
  • 雙向Channel可以在函數參數中以普通形式使用,進行發送和接收操作。
  • 範例:
ch := make(chan int)
go func() {
ch <- 42 // 向Channel發送資料
fmt.Println(<-ch) // 從Channel接收資料
}()

2. 單向Channel(Unidirectional Channels)

單向Channel只允許發送或接收操作,以增加程式的安全性和可讀性。它們可以分為兩種類型:

a. 單向發送Channel(Send-Only Channel)

  • 只允許發送操作,不允許接收操作。
  • 建立方式:ch := make(chan<- T),其中T是所需的資料類型。
  • 用來向其他goroutines發送資料,提高安全性。
  • 範例:
func sendData(ch chan<- int, data int) {
ch <- data
}

func main() {
ch := make(chan<- int)
go sendData(ch, 42) // 向單向發送Channel發送資料
// 這行程式碼無法編譯,因為我們無法從單向發送Channel接收資料
value := <-ch // 編譯錯誤
}

b. 單向接收Channel(Receive-Only Channel)

  • 只允許接收操作,不允許發送操作。
  • 建立方式:ch := make(<-chan T),其中T是所需的資料類型。
  • 用來限制goroutines只能從Channel接收資料,提高程式的可讀性。
  • 範例:
func receiveData(ch <-chan int) {
data := <-ch
fmt.Println(data)
}

func main() {
ch := make(<-chan int)
go receiveData(ch) // 從單向接收Channel接收資料
// 這行程式碼無法編譯,因為我們無法向單向接收Channel發送資料
ch <- 42 // 編譯錯誤
}

Channel的特性、限制與常見用法

特性

  1. 通訊機制:Channel是用於goroutines之間通訊的機制。它們允許goroutines安全地傳遞資料,確保同步和協調。
package main

import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
// 启动一个goroutine发送消息到Channel
go func() {
ch <- "Hello from goroutine!"
}()
// 主goroutine接收消息
msg := <-ch
fmt.Println(msg) // 输出:Hello from goroutine!
}

在這個例子中,我們建立了一個String類型的Channel ch,然後在一個goroutine中將消息發送到該Channel,主goroutine接收並打印消息。

2. 阻塞操作:Channel的發送(send)和接收(receive)操作都是阻塞的。如果發送方沒有goroutine準備好接收資料,或者接收方沒有資料可用,操作將被阻塞,直到滿足條件為止。

package main

import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go func() {
time.Sleep(2 * time.Second)
ch <- 42 // 將資料42發送到Channel
}()
select {
case value := <-ch:
fmt.Printf("接收到資料:%d\n", value)
case <-time.After(1 * time.Second):
fmt.Println("超時:未能接收到資料")
}
}

在範例中,我們先建立了一個Channel ch,然後啟動一個goroutine,該goroutine將在2秒後向Channel發送數字42。接著,我們使用select語句來等待Channel的資料接收操作,但我們設置了一個1秒的超時。

這裡可能發生以下情況:

  1. 如果在2秒內,goroutine成功向Channel發送了數字42,則我們會在case value := <-ch分支處接收到資料,並輸出「接收到資料:42」。
  2. 如果在1秒內無法接收到資料,則超時發生,我們會進入case <-time.After(1 * time.Second)分支,並輸出「超時:未能接收到資料」。

這個範例展示了Channel的阻塞操作特性,無論是成功接收資料還是超時,都取決於Channel中是否有可用的資料以及操作是否阻塞。這種阻塞操作使得在併發程式設計中能夠安全地等待資料的到來,而不需要使用額外的等待或輪詢機制。

3. 具有方向性:Channel可以是單向或雙向的。單向Channel只允許發送或接收操作,而雙向Channel則沒有這限制。

4. 緩衝Channel:可以使用make函數的第二個參數來建立緩衝Channel。緩衝Channel允許一定數量的資料在不阻塞的情況下進行發送,直到緩衝區滿為止。

package main

import "fmt"
func main() {
ch := make(chan int, 2) // 建立一個容量為2的緩衝Channel
ch <- 1 // 發送第一個數字,不會阻塞
ch <- 2 // 發送第二個數字,不會阻塞
// ch <- 3 // 如果再嘗試發送第三個數字,將會阻塞,因為緩衝區已滿
fmt.Println(<-ch) // 從Channel接收第一個數字
fmt.Println(<-ch) // 從Channel接收第二個數字
}

在這個範例中,我們建立了一個容量為2的緩衝Channel。我們可以連續發送兩個數字,而不會阻塞,但如果想再發送第三個數字,則會阻塞,直到有goroutine接收。

限制

  1. 只能傳遞相同類型的資料:就像紙條只講一件事,可能英文課上傳紙條會寫英文,數學課上傳紙條會寫數學;Channel也一樣,每個Channel只允許傳遞相同類型的資料。例如,如果建立了一個整數型的Channel,則只能發送和接收整數。
package main

import "fmt"
func main() {
ch := make(chan int)
// 以下程式碼將導致編譯錯誤
ch <- "Hello" // 嘗試向整數Channel發送String
}

在這個例子中,我們想要在整數型的Channel ch 傳遞一個String,這就會讓編譯器看不懂,進而報錯,因為它們不是相同類型的資料。

2. 不可擴展:一樣在拿紙條來舉例,當你撕下一張紙作為紙條,就不能再決定紙條大小了;Channel也是,建立完後,容量就不能動態擴展。如果需要更多容量,就必須重新建立一個新的Channel。

package main

import (
"fmt"
"time"
)
func main() {
// 建立一個整數型的Channel,容量為2
ch := make(chan int, 2)
// 向Channel發送兩個數字
ch <- 1
ch <- 2
// 嘗試再次向Channel發送,但此時容量已滿,將導致死鎖
// ch <- 3 // 這行會導致程式死鎖
// 讀取Channel中的數字
fmt.Println(<-ch)
fmt.Println(<-ch)
// 關閉Channel
close(ch)
// 再次嘗試讀取Channel中的數字,將得到零值並不會造成死鎖
fmt.Println(<-ch) // 輸出:0
}

常見用法

  1. 併發控制:可以使用Channel來協調多個goroutines的執行順序,確保它們按照所需的順序執行。
  2. 資料共享:多個goroutines可以通過Channel共享資料,而無需使用額外的同步機制,從而避免競爭條件和死鎖。
  3. 事件通知:Channel可以用於通知goroutines發生的事件,例如某個條件已滿足或某個任務已完成。
  4. 異步處理:可以使用Channel來啟動異步任務,然後等待它們完成,而不會阻塞主線程。

小結

Goroutine就像是講電話的我們,Channel就像是話筒,能穩穩地傳達我要的訊息,在goroutines之間安全地傳遞資料和協調操作,也避免了競爭條件和死鎖等問題。

📚Reference

17會員
83內容數
golang
留言0
查看全部
發表第一個留言支持創作者!
Alan的開發者天地 的其他內容
👨‍💻簡介 在日常生活中,如果能同時做很多事情,效率肯定大大提升,那麼在Go語言中,該如何做到呢,答案就是今天的主角Goroutine了,在Go語言中,讓併發變得簡單的強大工具,今天就是來給他一個快速介紹。
👨‍💻簡介 當我們在宣告變數時,電腦會為該變數在記憶體中分配一個位置,然後將這個變數值儲存在這個位置上,需要讀取或修改這個變數值時,電腦是透過記憶體位置來存取這個值。 今天來簡單介紹一下go的Pointer,他的特性以及常見用法。
👨‍💻簡介 在軟體開發中,錯誤無所不在。無論是網路請求失敗、檔案不存在,還是數學計算錯誤,處理錯誤是任何開發者的日常工作,系統的穩定度基本取決於對於錯誤處理是否全面,好的錯誤處理也可以產生適當的錯誤訊息,讓 Debug 更加容易。
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
👨‍💻簡介 在Go中,假如我要判斷一個資料類型是甚麼,該怎麼做呢? Golang有一個功能叫做Type Assertions(類型斷言),它的作用就是能夠在運行時檢查我的資料類型,讓我在傳遞類型時能確保資料類型是正確的。
👨‍💻簡介 make函數在slice、map和之後會介紹到的channel的初始化中扮演著關鍵的角色。本文將會簡單介紹make函數的用法,以及在初始化不同資料結構時的差異,讓你更好地理解和利用make函數。
👨‍💻簡介 在日常生活中,如果能同時做很多事情,效率肯定大大提升,那麼在Go語言中,該如何做到呢,答案就是今天的主角Goroutine了,在Go語言中,讓併發變得簡單的強大工具,今天就是來給他一個快速介紹。
👨‍💻簡介 當我們在宣告變數時,電腦會為該變數在記憶體中分配一個位置,然後將這個變數值儲存在這個位置上,需要讀取或修改這個變數值時,電腦是透過記憶體位置來存取這個值。 今天來簡單介紹一下go的Pointer,他的特性以及常見用法。
👨‍💻簡介 在軟體開發中,錯誤無所不在。無論是網路請求失敗、檔案不存在,還是數學計算錯誤,處理錯誤是任何開發者的日常工作,系統的穩定度基本取決於對於錯誤處理是否全面,好的錯誤處理也可以產生適當的錯誤訊息,讓 Debug 更加容易。
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
👨‍💻簡介 在Go中,假如我要判斷一個資料類型是甚麼,該怎麼做呢? Golang有一個功能叫做Type Assertions(類型斷言),它的作用就是能夠在運行時檢查我的資料類型,讓我在傳遞類型時能確保資料類型是正確的。
👨‍💻簡介 make函數在slice、map和之後會介紹到的channel的初始化中扮演著關鍵的角色。本文將會簡單介紹make函數的用法,以及在初始化不同資料結構時的差異,讓你更好地理解和利用make函數。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
關於15-5通道 人類設計系統是一個強大的自我理解工具,利用15-5通道作為理解個人獨特能量動態的關鍵組成部分。15-5通道,又稱為節奏之通道,連接著G中心(15號閘門)和薦骨中心(5號閘門)。這個通道攜帶著一種獨特能量的結合,可以影響個人對生活、日常和節奏的方式。 15號閘門 位於G中心,也
Thumbnail
如果想把YouTube 頻道轉成RSS連結的話,需要Channel ID。以前只要看網址就可以知道ID了,現在步驟比較多,分享一下。 進到要訂閱的頻道首頁,點開詳細介紹 點開後會長這樣 滑到底,按「分享頻道」 就能選擇「複製頻道ID」了
我的You Tube Channel: Lu,chunmei 盧春美
My you Tube channel is Tsai,bingfan(蔡賓芳)
Thumbnail
👨‍💻簡介 昨天講到Goroutine有稍微簡單介紹Channel,Channel是Go語言中極為重要的併發通訊機制,它就像是不同goroutines之間的話筒,允許它們安全地傳遞資料和信息。這個強大的工具使得Go語言在處理併發任務時非常優雅和高效。
Thumbnail
禮拜一又要返office喇,你哋個週末過成點呢? 😊呢個就係我哋上個禮拜嘅財經新聞摘要!✨ 1️⃣ 香港2023年第二季經濟喺旅遊同消費帶動下,年增1.5%嘅GDP增長,但整體貨品出口同投資支出有所下降,因為全球情況可能會面對出口挑戰🌍。🔗 https://bit.ly/3KFc2Mm 2
Thumbnail
週一快樂! 🌟 呢個係我哋上個禮拜一啲財經新聞嘅每週摘要: 1️⃣根據羅伯特·沃爾特斯 (Robert Walters) 最近的一項研究,在就業市場不確定性的推動下,28% 的專業人士從事第二職業以提高收入。他們將額外的收入分配給💰生活費、💸儲蓄、😍追求興趣。🔗 https://bit.
Thumbnail
星期一快樂!剛剛過去的周末太熱了♨️,我整天呆在家裡享受著空調💨。這是我們上週的一些財經新聞的每週摘要: 1)      為應對美聯儲近期加息25個基點,香港金融管理局(金管局)已將基準利率上調至5.75%,由7月27日起生效。金管局的基本利率是根據預設公式釐定,該公式考慮了美聯儲利率的現行目標
Thumbnail
週一快樂(希望)!🎉 你們的週末過得如何?現在到了我們每週一次的財經快訊時間了!📰以下是一些重要的財經新聞💰: 1)香港2023年6月份的出口價值同比下降11.4%至超過3000多億📉,主要是電氣和辦公機械,出口至亞洲、德國、美國和英國🌍,略有改善,但全球增長疲軟的壓力持續存在💪。🔗
Thumbnail
我們要以積極的心態迎接新的一週!以下是財經快線☄️ 1) 香港公司註冊處報告稱,2023年上半年本地公司新增72,588家,總數達到142萬家。 4月實施的《2 023年公司(修訂)條例》為召開股東大會提供了更大的靈活性。此外,共有422家外國公司在香港設立分支機構,註冊的非香港公司總數達14,6
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
11/20日NVDA即將公布最新一期的財報, 今天Sell Side的分析師, 開始調高目標價, 市場的股價也開始反應, 未來一週NVDA將重新回到美股市場的焦點, 今天我們要分析NVDA Sell Side怎麼看待這次NVDA的財報預測, 以及實際上Buy Side的倉位及操作, 從
Thumbnail
Hi 大家好,我是Ethan😊 相近大家都知道保濕是皮膚保養中最基本,也是最重要的一步。無論是在畫室裡長時間對著畫布,還是在旅途中面對各種氣候變化,保持皮膚的水分平衡對我來說至關重要。保濕化妝水不僅能迅速為皮膚補水,還能提升後續保養品的吸收效率。 曾經,我的保養程序簡單到只包括清潔和隨意上乳液
關於15-5通道 人類設計系統是一個強大的自我理解工具,利用15-5通道作為理解個人獨特能量動態的關鍵組成部分。15-5通道,又稱為節奏之通道,連接著G中心(15號閘門)和薦骨中心(5號閘門)。這個通道攜帶著一種獨特能量的結合,可以影響個人對生活、日常和節奏的方式。 15號閘門 位於G中心,也
Thumbnail
如果想把YouTube 頻道轉成RSS連結的話,需要Channel ID。以前只要看網址就可以知道ID了,現在步驟比較多,分享一下。 進到要訂閱的頻道首頁,點開詳細介紹 點開後會長這樣 滑到底,按「分享頻道」 就能選擇「複製頻道ID」了
我的You Tube Channel: Lu,chunmei 盧春美
My you Tube channel is Tsai,bingfan(蔡賓芳)
Thumbnail
👨‍💻簡介 昨天講到Goroutine有稍微簡單介紹Channel,Channel是Go語言中極為重要的併發通訊機制,它就像是不同goroutines之間的話筒,允許它們安全地傳遞資料和信息。這個強大的工具使得Go語言在處理併發任務時非常優雅和高效。
Thumbnail
禮拜一又要返office喇,你哋個週末過成點呢? 😊呢個就係我哋上個禮拜嘅財經新聞摘要!✨ 1️⃣ 香港2023年第二季經濟喺旅遊同消費帶動下,年增1.5%嘅GDP增長,但整體貨品出口同投資支出有所下降,因為全球情況可能會面對出口挑戰🌍。🔗 https://bit.ly/3KFc2Mm 2
Thumbnail
週一快樂! 🌟 呢個係我哋上個禮拜一啲財經新聞嘅每週摘要: 1️⃣根據羅伯特·沃爾特斯 (Robert Walters) 最近的一項研究,在就業市場不確定性的推動下,28% 的專業人士從事第二職業以提高收入。他們將額外的收入分配給💰生活費、💸儲蓄、😍追求興趣。🔗 https://bit.
Thumbnail
星期一快樂!剛剛過去的周末太熱了♨️,我整天呆在家裡享受著空調💨。這是我們上週的一些財經新聞的每週摘要: 1)      為應對美聯儲近期加息25個基點,香港金融管理局(金管局)已將基準利率上調至5.75%,由7月27日起生效。金管局的基本利率是根據預設公式釐定,該公式考慮了美聯儲利率的現行目標
Thumbnail
週一快樂(希望)!🎉 你們的週末過得如何?現在到了我們每週一次的財經快訊時間了!📰以下是一些重要的財經新聞💰: 1)香港2023年6月份的出口價值同比下降11.4%至超過3000多億📉,主要是電氣和辦公機械,出口至亞洲、德國、美國和英國🌍,略有改善,但全球增長疲軟的壓力持續存在💪。🔗
Thumbnail
我們要以積極的心態迎接新的一週!以下是財經快線☄️ 1) 香港公司註冊處報告稱,2023年上半年本地公司新增72,588家,總數達到142萬家。 4月實施的《2 023年公司(修訂)條例》為召開股東大會提供了更大的靈活性。此外,共有422家外國公司在香港設立分支機構,註冊的非香港公司總數達14,6