<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Golang 中的通道是一種高效、安全、靈活的並行機制,用於在並行環境下實現資料的同步和傳遞。通道提供了一個執行緒安全的佇列,只允許一個 goroutine 進行讀操作,另一個 goroutine 進行寫操作。通過這種方式,通道可以有效地解決並行程式設計中的競態條件、鎖問題等常見問題。
通道有兩種型別:有緩衝通道和無緩衝通道。在通道建立時,可以指定通道的容量,即通道緩衝區的大小,如果不指定則預設為無緩衝通道。
Golang 通道的基本語法非常簡單,使用 make 函數來建立一個通道:
ch := make(chan int)
這行程式碼建立了一個名為 ch 的通道,通道的資料型別為 int。通道的讀寫操作可以使用箭頭符號 <-,<- 表示從通道中讀取資料,-> 表示向通道中寫入資料。例如:
ch := make(chan int) ch <- 1 // 向通道中寫入資料1 x := <- ch // 從通道中讀取資料,並賦值給變數x
在 Golang 中,通道還支援緩衝機制。通道的緩衝區可以儲存一定量的資料,當緩衝區滿時,向通道寫入資料將阻塞。當通道緩衝區為空時,從通道讀取資料將阻塞。使用緩衝機制可以增加程式的靈活性和並行效能。
緩衝區大小為 0 的通道稱為無緩衝通道。無緩衝通道的傳送和接收操作都是阻塞的,因此必須有接收者準備好接收才能進行傳送操作,反之亦然。這種機制確保了通道的同步性,即在通道操作前後,傳送者和接收者都會被阻塞,直到對方做好準備。
有緩衝通道的建立方式為:
ch := make(chan int, 3)
這行程式碼建立了一個名為 ch 的通道,通道的資料型別為 int,通道緩衝區的大小為 3。向有緩衝通道寫入資料時,如果緩衝區未滿,則寫操作將成功,程式將繼續執行。如果緩衝區已滿,則寫操作將阻塞,直到有空閒緩衝區可用。
從有緩衝通道讀取資料時,如果緩衝區不為空,則讀操作將成功,程式將繼續執行。如果緩衝區為空,則讀操作將阻塞,直到有資料可讀取。
無緩衝通道的建立方式為:
ch := make(chan int)
這行程式碼建立了一個名為ch的通道,通道的資料型別為 int,通道緩衝區的大小為 0。無緩衝通道的傳送和接收操作都是阻塞的,因此必須有接收者準備好接收才能進行傳送操作,反之亦然。
在並行程式設計中,常常需要對通道進行超時和計時操作。Golang 中提供了 time 包來實現超時和計時器。
在 Golang 中,可以使用 select 語句和 time.After 函數來實現通道的超時操作。例如:
select { case data := <-ch: fmt.Println(data) case <-time.After(time.Second): fmt.Println("timeout") }
這段程式碼中,select 語句監聽了通道 ch 和 time.After(time.Second) 兩個通道,如果 ch 中有資料可讀,則讀取並輸出資料;如果等待 1 秒鐘後仍然沒有資料,則超時並輸出 timeout。
Golang 中提供了 time 包來實現計時器機制。可以使用 time.NewTimer(duration) 函數建立一個計時器,計時器會在 duration 時間後觸發一個定時事件。例如:
timer := time.NewTimer(time.Second * 2) <-timer.C fmt.Println("Timer expired")
這段程式碼建立了一個計時器,設定時間為 2 秒鐘,當計時器到達 2 秒鐘時,會向 timer.C 通道中傳送一個定時事件,程式通過 <-timer.C 語句等待定時事件的到來,並在接收到定時事件後輸出 “Timer expired”。
在 Golang 中,通道是一種參照型別,可以像普通變數一樣進行傳遞。例如:
func worker(ch chan int) { data := <-ch fmt.Println(data) } func main() { ch := make(chan int) go worker(ch) ch <- 1 time.Sleep(time.Second) }
這段程式碼中,main 函數中建立了一個名為ch的通道,並啟動了一個 worker goroutine,向 ch 通道中寫入了一個資料 1。worker goroutine 中通過 <-ch 語句從 ch 通道中讀取資料,並輸出到控制檯中。
在 Golang 中,可以通過使用單向通道來限制通道的讀寫操作。單向通道只允許讀或寫操作,不允許同時進行讀寫操作。例如:
func producer(ch chan<- int) { ch <- 1 } func consumer(ch <-chan int) { data := <-ch fmt.Println(data) } func main() { ch := make(chan int) go producer(ch) go consumer(ch) time.Sleep(time.Second) }
這段程式碼中,produce r函數和 consumer 函數分別用於向通道中寫入資料和從通道中讀取資料。在函數的引數中,使用了單向通道限制引數的讀寫操作。在 main 函數中,建立了一個名為 ch 的通道,並啟動了一個 producer goroutine 和一個 consumer goroutine,producer 向 ch 通道中寫入資料1,consumer 從 ch 通道中讀取資料並輸出到控制檯中。
在 Golang 中,可以使用 close 函數來關閉通道。關閉通道後,通道的讀寫操作將會失敗,讀取通道將會得到零值,寫入通道將會導致 panic 異常。例如:
ch := make(chan int) go func() { for i := 0; i < 5; i++ { ch <- i } close(ch) }() for data := range ch { fmt.Println(data) }
這段程式碼中,建立了一個名為 ch 的通道,並在一個 goroutine 中向通道中寫入資料 0 到 4,並通過 close 函數關閉通道。在主 goroutine 中,通過 for...range 語句迴圈讀取通道中的資料,並輸出到控制檯中,當通道被關閉時,for...range 語句會自動退出迴圈。
在關閉通道後,仍然可以從通道中讀取已經存在的資料,例如:
ch := make(chan int) go func() { for i := 0; i < 5; i++ { ch <- i } close(ch) }() for { data, ok := <-ch if !ok { break } fmt.Println(data) }
這段程式碼中,通過迴圈讀取通道中的資料,並判斷通道是否已經被關閉。當通道被關閉時,讀取操作將會失敗,ok 的值將會變為 false,從而退出迴圈。
通道是 Golang 並行程式設計中的重要組成部分,其常見的應用場景包括:
通道可以被用來在不同的 goroutine 之間同步資料。當一個 goroutine 需要等待另一個goroutine 的結果時,可以使用通道進行資料的傳遞。例如:
package main import "fmt" func calculate(a, b int, result chan int) { result <- a + b } func main() { result := make(chan int) go calculate(10, 20, result) fmt.Println(<-result) }
在這個例子中,我們使用通道來進行 a+b 的計算,並將結果傳送給主函數。在主函數中,我們等待通道中的結果並輸出。
通道也可以用於協調多個 goroutine 之間的操作。例如,在一個生產者-消費者模式中,通道可以作為生產者和消費者之間的緩衝區,協調資料的生產和消費。例如:
package main import ( "fmt" "sync" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Println("worker", id, "processing job", j) results <- j * 2 } } func main() { jobs := make(chan int, 100) results := make(chan int, 100) // 開啟三個worker goroutine for w := 1; w <= 3; w++ { go worker(w, jobs, results) } // 傳送9個任務到jobs通道中 for j := 1; j <= 9; j++ { jobs <- j } close(jobs) // 輸出每個任務的結果 for a := 1; a <= 9; a++ { <-results } }
在這個例子中,我們使用通道來協調三個 worker goroutine 之間的任務處理。每個 worker goroutine 從 jobs 通道中獲取任務,並將處理結果傳送到 results 通道中。主函數負責將所有任務傳送到 jobs 通道中,並等待所有任務的結果返回。
當多個 goroutine 需要並行存取某些共用資源時,通道可以用來控制並行存取。通過使用通道,可以避免出現多個 goroutine 同時存取共用資源的情況,從而提高程式的可靠性和效能。例如:
package main import ( "fmt" "sync" ) var ( balance int wg sync.WaitGroup mutex sync.Mutex ) func deposit(amount int) { mutex.Lock() balance += amount mutex.Unlock() wg.Done() } func main() { for i := 0; i < 1000; i++ { wg.Add(1) go deposit(100) } wg.Wait() fmt.Println("balance:", balance) }
在這個例子中,我們使用互斥鎖來控制對 balance 變數的並行存取。每個 goroutine 負責將 100 元存入 balance 變數中。使用互斥鎖可以確保在任意時刻只有一個 goroutine 能夠存取 balance 變數。
通道也可以用來模擬事件驅動的機制。例如,可以使用通道來模擬一個事件佇列,當某個事件發生時,可以將事件資料放入通道中,然後通過另一個 goroutine 來處理該事件。例如:
package main import ( "fmt" "time" ) func eventLoop(eventChan <-chan string) { for { select { case event := <-eventChan: fmt.Println("Event received:", event) case <-time.After(5 * time.Second): fmt.Println("Timeout reached") return } } } func main() { eventChan := make(chan string) // 模擬事件發生 go func() { time.Sleep(2 * time.Second) eventChan <- "Event 1" time.Sleep(1 * time.Second) eventChan <- "Event 2" time.Sleep 1 * time.Second eventChan <- "Event 3" time.Sleep(4 * time.Second) eventChan <- "Event 4" }() eventLoop(eventChan) }
在這個例子中,我們使用通道來模擬事件的發生。eventLoop 函數使用 select 語句監聽 eventChan 通道和 5 秒超時事件。當 eventChan 收到事件時,eventLoop 函數將事件列印出來。如果 5 秒內沒有收到事件,則 eventLoop 函數結束。主函數負責建立 eventChan 通道,並模擬事件的發生。
package main import ( "fmt" "sync" ) func processTask(task int) { fmt.Println("Processing task", task) } func main() { tasks := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // 定義並行數為3的批次處理常式 batchSize := 3 var wg sync.WaitGroup taskChan := make(chan int) for i := 0; i < batchSize; i++ { wg.Add(1) go func() { defer wg.Done() for task := range taskChan { processTask(task) } }() } // 將任務分發到taskChan通道中 for _, task := range tasks { taskChan <- task } close(taskChan) wg.Wait() }
在這個例子中,我們使用通道來批次處理任務。首先定義了一個包含 10 個任務的陣列。然後,我們定義了一個並行數為 3 的批次處理常式,它從 taskChan 通道中獲取任務,並將任務處理結果輸出。主函數負責將所有任務傳送到 taskChan 通道中,並等待所有任務處理結束。注意,我們使用了 sync.WaitGroup 來等待所有批次處理常式的 goroutine 結束。
package main import "fmt" type eventBus struct { subscriptions map[string][]chan string } func newEventBus() *eventBus { return &eventBus{ subscriptions: make(map[string][]chan string), } } func (eb *eventBus) subscribe(eventType string, ch chan string) { eb.subscriptions[eventType] = append(eb.subscriptions[eventType], ch) } func (eb *eventBus) unsubscribe(eventType string, ch chan string) { subs := eb.subscriptions[eventType] for i, sub := range subs { if sub == ch { subs[i] = nil eb.subscriptions[eventType] = subs[:i+copy(subs[i:], subs[i+1:])] break } } } func (eb *eventBus) publish(eventType string, data string) { for _, ch := range eb.subscriptions[eventType] { if ch != nil { ch <- data } } } func main() { eb := newEventBus() ch1 := make(chan string) ch2 := make(chan string) eb.subscribe("event1", ch1) eb.subscribe("event2", ch2) go func() { for { select { case data := <-ch1: fmt.Println("Received event1:", data) case data := <-ch2: fmt.Println("Received event2:", data) } } }() eb.publish("event1", "Event 1 data") eb.publish("event2", "Event 2 data") eb.unsubscribe("event1", ch1) eb.publish("event1", "Event 1 data after unsubscribe") // 等待事件處理完成 fmt.Scanln() }
在這個例子中,我們使用通道來實現釋出/訂閱模式。定義了一個 eventBus 結構體,它包含了一個 subscriptions map,用來儲存事件型別和訂閱該事件型別的所有通道。我們可以通過 subscribe 函數向某個事件型別新增訂閱通道,通過 unsubscribe 函數取消訂閱通道,通過 publish 函數向某個事件型別釋出事件。
在主函數中,我們建立了兩個通道 ch1 和 ch2,並通過 subscribe 函數訂閱了 "event1" 和 "event2" 兩個事件型別。然後,我們啟動了一個 goroutine,使用 select 語句監聽 ch1 和 ch2 通道,將接收到的事件列印出來。接著,我們使用 publish 函數分別向 "event1" 和 "event2" 釋出了事件。最後,我們使用 unsubscribe 函數取消了對 "event1" 事件型別的 ch1 通道的訂閱,再次使用 publish 函數向 "event1" 釋出了事件。注意,我們使用了 fmt.Scanln() 來等待事件處理完成,以避免程式在事件處理完畢前退出。
通道是 Go 中非常重要的並行原語,可以有效地管理並行存取共用資料,避免資料競爭。通過通道,可以實現同步和非同步的訊息傳遞,實現不同 goroutine 之間的通訊。在使用通道時,需要注意通道的基本語法、緩衝機制、超時和計時器、通道的傳遞、單向通道和關閉通道等知識點,並根據實際場景選擇合適的通道模式,以提高程式的並行效能和穩定性。
以上就是超實用的Golang通道指南之輕鬆實現並行程式設計的詳細內容,更多關於Golang通道實現並行程式設計的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45