Go語言中Goroutine的等待方式

更新於 發佈於 閱讀時間約 9 分鐘


raw-image

👨‍💻簡介

昨天講到Goroutine的橋梁aka傳話筒 — Channel,那要怎麼知道對方有收到訊息,我的紙條有送到對方手上呢? 今天就是要來介紹幾種Goroutine的確定完成工作的幾種方式。

Goroutine 的等待方式

在Go語言中,等待Goroutine完成的方式有多種。

sync.WaitGroup

sync.WaitGroup是Go語言中常見的一種等待方式,它允許我們等待一組Goroutine完成。

package main

import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 表示Goroutine完成工作,減少WaitGroup計數
fmt.Printf("Worker %d 正在工作\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // 增加WaitGroup數量
go worker(i, &wg) // 啟動Goroutine,並將WaitGroup的指針傳遞給它
}
wg.Wait() // 將主程式阻塞,直到所有Goroutines完成工作。
fmt.Println("所有工作完成")
}

在上面的範例中,我們先創建了一個sync.WaitGroup的變數wg,並使用for循環去執行我們的goroutine,每次執行前都先將等待goroutine的數量+1,總共等待三個Goroutine完成工作。 接著使用 wg.Wait()將main函數先暫停一下,等所有goroutine都完成工作,wg的計數器為0,在繼續執行程式。 在 worker函數裡,會接收 wg的pointer,並在開頭使用defer wg.Done()在程式執行結束就將等待的goroutine數量-1,來確保goroutine會完成

Channel

Channel是Go語言中用於通信的機制,也可以用於等待Goroutine完成。

package main

import (
"fmt"
)
func worker(id int, ch chan bool) {
fmt.Printf("Worker %d 正在工作\n", id)
ch <- true
}
func main() {
ch := make(chan bool)
for i := 1; i <= 3; i++ {
go worker(i, ch)
}
for i := 1; i <= 3; i++ {
<-ch
}
fmt.Println("所有工作完成")
}

Channel的特性就是有方向性,在這段程式碼中,創建了三個Goroutines,每個Goroutine在執行時都印出一條訊息,然後將一個布林值傳送到Channel中,最後等三個Channel都接收到操作完成後,印出 “所有工作完成”。

select

使用select語句可以等待多個Goroutine中的任何一個完成。

package main

import (
"fmt"
"time"
)
func worker(id int, ch chan bool) {
fmt.Printf("Worker %d 正在工作\n", id)
time.Sleep(time.Second * time.Duration(id))
ch <- true
}
func main() {
ch1 := make(chan bool)
ch2 := make(chan bool)
go worker(1, ch1)
go worker(2, ch2)
select {
case <-ch1:
fmt.Println("Worker 1 完成")
case <-ch2:
fmt.Println("Worker 2 完成")
}
fmt.Println("工作完成")
}

在這個範例中,我們啟動了兩個Goroutines(worker(1, ch1) 和 worker(2, ch2)),它們各自執行一些工作並將布林值寫入Channel。接著,我們使用 select 來等待哪個Channel中的值會先到達,即哪個Goroutine先完成工作。

select的特性, 只會選擇其中一個 case,如果兩個Goroutines都完成,只會選擇一個來處理,而不會等待所有Goroutines完成。這也使得 select 非常適合用來需要競爭的情況,可以根據需要增加更多的 case 來等待多個Goroutines。

time.Sleep

time.Sleep也是一種簡單的等待方式,但它只是預測多久完成,而且會阻塞整個執行緒,不推。

package main

import (
"fmt"
"time"
)
func worker(id int) {
fmt.Printf("Worker %d 正在工作\n", id)
time.Sleep(time.Second * time.Duration(id))
fmt.Printf("Worker %d 完成\n", id)
}
func main() {
go worker(1)
go worker(2)
// 等待一段時間以確保工作完成
time.Sleep(time.Second * 3)
fmt.Println("所有工作完成")
}

Goroutine 等待方式的比較

不同的等待方式有各自的使用場景和優缺點。sync.WaitGroup是最常用的方式,因為它簡單且高效。Channel等待方式在需要更多控制時很有用,而select則適用於等待多個Goroutine中的某一個。請根據您的需求選擇最適合的方式。

當你在Go語言中使用Goroutines並需要等待它們完成時,有幾種不同的等待方式可供選擇。以下是這些等待方式的比較:

  1. sync.WaitGroup

優點

  • 簡單易用
  • 效率高:等待期間不會浪費CPU資源。
  • 可用於任意數量的Goroutines。

限制

  • 需要額外的記憶體:每個等待的Goroutine都需要一個WaitGroup。
  • 只能等待固定數量的Goroutines。

2. Channel(Channel)等待

優點

  • 可以靈活控制等待:你可以在Goroutine完成時向Channel發送信號。
  • 可以用於不定數量的Goroutines。
  • 可以用於任何需要自定義等待邏輯的情況。

限制

  • 需要額外的程式碼來處理Channel。
  • 可能需要額外的同步以確保所有Channel操作正確。

3. select 多Channel選擇

優點

  • 可以等待多個Goroutines中的任何一個完成。
  • 適用於需要競爭的情況。

限制

  • 複雜性:當你有多個Channel時,可能需要更多的程式碼和邏輯。
  • 只能等待一個完成,無法等待多個Goroutines全部完成。

4. time.Sleep

優點

  • 簡單明瞭,容易理解。

限制

  • 不是一種有效的等待方式,因為會阻塞整個函數,浪費CPU資源。
  • 無法確保Goroutines何時完成。
  • 不推薦在正式環境中使用。

這樣看下來,最常見且推薦的方式就是使用sync.WaitGroup,因為簡單而且高效。但根據不同情況,你可能需要使用其他方式。使用Channel等待和select適用於需要更多控制和競爭的情況,而time.Sleep只應該在測試或學習目的中使用。選擇適合你需求的等待方式,才能確保Goroutines的並發能夠在你的應用中正確運作。

小結

前兩篇介紹了Goroutine、Channel,最後就來介紹一下如何確保你的工作有完成,紙條有成功送到對方手上。了解如何等待它們完成是開發高效併發應用的重要一環。介紹了多種等待Goroutine的方式,每種方式都有他的優勢和限制。可以根據需求,選擇適合的方式來讓你程式碼能夠充分利用Goroutine的優點。

📚Reference

留言
avatar-img
留言分享你的想法!
阿Han-avatar-img
2023/09/15
官方的代碼區塊好像都會跑版XD
wang alan-avatar-img
發文者
2023/09/18
阿Han 確實,最近在來手動修一下
avatar-img
Alan的開發者天地
18會員
83內容數
golang
Alan的開發者天地的其他內容
2023/10/04
👨‍💻 簡介 最近想要透過小實作來撰寫筆記,達到做中學的效果,因此就來實作個小爬蟲順便結合前面學到的package做一個小複習。
Thumbnail
2023/10/04
👨‍💻 簡介 最近想要透過小實作來撰寫筆記,達到做中學的效果,因此就來實作個小爬蟲順便結合前面學到的package做一個小複習。
Thumbnail
2023/09/28
👨‍💻簡介 今天來介紹一個自己開發後端蠻常用的一個 package,promptui,拿來做menu真的很方便,promptui有兩個主要的輸入模式: Prompt:跳出單行使用者輸入。 Select:提供一個選項列表供使用者選擇。
Thumbnail
2023/09/28
👨‍💻簡介 今天來介紹一個自己開發後端蠻常用的一個 package,promptui,拿來做menu真的很方便,promptui有兩個主要的輸入模式: Prompt:跳出單行使用者輸入。 Select:提供一個選項列表供使用者選擇。
Thumbnail
2023/09/26
👨‍💻 簡介 在處理string時,正則表達式是一個非常有用的工具。Go語言的regexp package 可以使用正則表達式,用來執行如檢查string是否匹配某個模式、提取匹配的subString等操作。
Thumbnail
2023/09/26
👨‍💻 簡介 在處理string時,正則表達式是一個非常有用的工具。Go語言的regexp package 可以使用正則表達式,用來執行如檢查string是否匹配某個模式、提取匹配的subString等操作。
Thumbnail
看更多
你可能也想看
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
大家好,我是一名眼科醫師,也是一位孩子的媽 身為眼科醫師的我,我知道視力發展對孩子來說有多關鍵。 每到開學季時,診間便充斥著許多憂心忡忡的家屬。近年來看診中,兒童提早近視、眼睛疲勞的案例明顯增加,除了3C使用過度,最常被忽略的,就是照明品質。 然而作為一位媽媽,孩子能在安全、舒適的環境
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
我的「媽」呀! 母親節即將到來,vocus 邀請你寫下屬於你的「媽」故事——不管是紀錄爆笑的日常,或是一直想對她表達的感謝,又或者,是你這輩子最想聽她說出的一句話。 也歡迎你曬出合照,分享照片背後的點點滴滴 ♥️ 透過創作,將這份情感表達出來吧!🥹
Thumbnail
嗨,開發者們!在網頁應用的世界中,即時通訊是一個越來越重要的特點。WebSocket提供了一個高效的方式來支持這種通訊,並且能夠在客戶端和伺服器之間提供雙向的即時通訊。本篇文章將會帶你走進WebSocket的世界,並學習如何在Gin框架中實現它。
Thumbnail
嗨,開發者們!在網頁應用的世界中,即時通訊是一個越來越重要的特點。WebSocket提供了一個高效的方式來支持這種通訊,並且能夠在客戶端和伺服器之間提供雙向的即時通訊。本篇文章將會帶你走進WebSocket的世界,並學習如何在Gin框架中實現它。
Thumbnail
👨‍💻簡介 要處理日期和時間,就必須知道這個Package -> time,Go提供了內建的timePackage。 今天主要介紹time的功能,包括時間操作、格式化等等,以及常見用法。
Thumbnail
👨‍💻簡介 要處理日期和時間,就必須知道這個Package -> time,Go提供了內建的timePackage。 今天主要介紹time的功能,包括時間操作、格式化等等,以及常見用法。
Thumbnail
👨‍💻簡介 昨天講到Goroutine的橋梁aka傳話筒 — Channel,那要怎麼知道對方有收到訊息,我的紙條有送到對方手上呢? 今天就是要來介紹幾種Goroutine的確定完成工作的幾種方式。
Thumbnail
👨‍💻簡介 昨天講到Goroutine的橋梁aka傳話筒 — Channel,那要怎麼知道對方有收到訊息,我的紙條有送到對方手上呢? 今天就是要來介紹幾種Goroutine的確定完成工作的幾種方式。
Thumbnail
👨‍💻簡介 昨天講到Goroutine有稍微簡單介紹Channel,Channel是Go語言中極為重要的併發通訊機制,它就像是不同goroutines之間的話筒,允許它們安全地傳遞資料和信息。這個強大的工具使得Go語言在處理併發任務時非常優雅和高效。
Thumbnail
👨‍💻簡介 昨天講到Goroutine有稍微簡單介紹Channel,Channel是Go語言中極為重要的併發通訊機制,它就像是不同goroutines之間的話筒,允許它們安全地傳遞資料和信息。這個強大的工具使得Go語言在處理併發任務時非常優雅和高效。
Thumbnail
👨‍💻簡介 在日常生活中,如果能同時做很多事情,效率肯定大大提升,那麼在Go語言中,該如何做到呢,答案就是今天的主角Goroutine了,在Go語言中,讓併發變得簡單的強大工具,今天就是來給他一個快速介紹。
Thumbnail
👨‍💻簡介 在日常生活中,如果能同時做很多事情,效率肯定大大提升,那麼在Go語言中,該如何做到呢,答案就是今天的主角Goroutine了,在Go語言中,讓併發變得簡單的強大工具,今天就是來給他一個快速介紹。
Thumbnail
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
Thumbnail
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
Thumbnail
👨‍💻簡介 在 Go 語言中,函數(Function)是一個強大且重要的概念,就像食譜一樣,告訴你應該如何處理食材,最後得到一道美味的料理。經過哪些程序讓程式更有組織性和可讀性。函數可幫助你將程式碼區塊組織成可重複使用的元件,進而執行特定的任務。
Thumbnail
👨‍💻簡介 在 Go 語言中,函數(Function)是一個強大且重要的概念,就像食譜一樣,告訴你應該如何處理食材,最後得到一道美味的料理。經過哪些程序讓程式更有組織性和可讀性。函數可幫助你將程式碼區塊組織成可重複使用的元件,進而執行特定的任務。
Thumbnail
當我們在處理一連串的事件或請求時,責任鏈模式 (Chain of Responsibility Pattern) 就像一位隱形的助手🤖,幫助我們將這些請求一一分發給合適的處理者。
Thumbnail
當我們在處理一連串的事件或請求時,責任鏈模式 (Chain of Responsibility Pattern) 就像一位隱形的助手🤖,幫助我們將這些請求一一分發給合適的處理者。
Thumbnail
介紹 委派的非同步方法 可以透過BeginInvoke執行委派的非同步方法 Action<T>.BeginInvoke(<T> obj,AsyncCallback callback,Object @object) 第一個內容的 obj,只的是要傳入acction委派的參數 第二個AsyncCallb
Thumbnail
介紹 委派的非同步方法 可以透過BeginInvoke執行委派的非同步方法 Action<T>.BeginInvoke(<T> obj,AsyncCallback callback,Object @object) 第一個內容的 obj,只的是要傳入acction委派的參數 第二個AsyncCallb
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News