探索Goroutine:Go語言的併發魔法

更新於 發佈於 閱讀時間約 8 分鐘
raw-image

👨‍💻簡介

在日常生活中,如果能同時做很多事情,效率肯定大大提升,那麼在Go語言中,該如何做到呢,答案就是今天的主角Goroutine了,在Go語言中,讓併發變得簡單的強大工具,今天就是來給他一個快速介紹。

什麼是Goroutine?

首先,讓我們以一個簡單的方式來解釋什麼是Goroutine。Goroutine是Go語言的一個特別的功能,它就像是小型的工作任務,可以讓我們同時處理很多事情,而不需要浪費太多電腦資源。可以把它想像成比傳統方式更聰明的方式來處理多項工作,而不會讓電腦變得超級忙碌。這種功能讓Go語言在處理大量同時執行的工作時變得非常強大。

如何創建和啟動Goroutine

要使用Goroutine很簡單,只需要創建一個函數,然後使用go關鍵字在要使用goroutine的函數前面就完成了。可以看以下範例:

package main

import (
"fmt"
)
func main() {
go sayHello()
// 主程式不會等待Goroutine完成
}
func sayHello() {
fmt.Println("Hello, Goroutine!")
}

在這個例子中,我們使用go關鍵字啟動一個新的Goroutine,Goroutine執行了sayHello函數。但須注意,主程式main不會等待Goroutine完成,所以可能不會看到有印出任何東西來。

可以稍微讓main主程式睡一下,就可以看到輸出了

package main

import (
"fmt"
"time"
)
func main() {
go sayHello()
time.Sleep(1)
}
func sayHello() {
fmt.Println("Hello, Goroutine!")
}

Goroutine的執行過程

下面提供了一個簡單的小程式,在主程式裡有兩個goroutine,試著執行一下程式可以發現輸出是一段一段的,會是0跟1交錯印出,代表兩邊的goroutine會搶著印出,呈現競爭狀態

package main

import (
"fmt"
"time"
)
func main() {
go print1()
go print2()
time.Sleep(time.Second)
}
func print1() {
for i := 0; i < 100; i++ {
fmt.Print("0")
}
}
func print2() {
for i := 0; i < 100; i++ {
fmt.Print("1")
}
}

Goroutine之間的通信

併發往往涉及到多個任務之間的通信。這就是通道(Channel)的用武之地。通道是一種特殊的資料結構,用於在不同的Goroutine之間傳遞資料:

package main

import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go sendData(ch)
go receiveData(ch)
time.Sleep(2 * time.Second) // 等待Goroutines完成
}
func sendData(ch chan string) {
ch <- "Hello from Goroutine!"
}
func receiveData(ch chan string) {
msg := <-ch
fmt.Println(msg)
}

在這個例子中,我們先創建了一個通道ch,然後使用通道在兩個Goroutine之間傳遞消息。 通道的溝通可以看到範例,<- chan 代表將資料從channel中取出,而chan <- 則代表將資料放進channel

Goroutine同步與等待

通常情況下,我們希望主程式能夠等待所有的Goroutine完成,以確保結果的完整性。這就是WaitGroup的作用:

sync.WaitGroup

這個函數的主要功能是讓主程式等待所有的Goroutine完成,然後再繼續執行接下來的程式,主要有以下幾種方法

  • Add(delta int):用來增加計數器的值,表示有多少個Goroutine需要等待。
  • Done():用來減少計數器的值,表示一個Goroutine已經完成。通常在Goroutine執行完後使用 Done
  • Wait():用來將計數器歸零。當計數器的值為零時,Wait 函數會返回,並允許主程式繼續執行。

接著讓我們來看一下範例:

package main

import (
"fmt"
"sync"
)
func main() {
// 創建一個WaitGroup
var wg sync.WaitGroup
// 啟動5個Goroutine
for i := 0; i < 5; i++ {
wg.Add(1) // 增加計數器
go worker(i, &wg)
}
wg.Wait() // 等待所有Goroutines完成
fmt.Println("All Goroutines have finished.")
}
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 減少計數器
fmt.Printf("Worker %d is done\n", id)
}

可以看到,我們使用WaitGroup來等待所有的Goroutine完成,確保我的主程式可以完整執行而不會提前退出。

Goroutine 的特性與限制

特性

  1. 資源消耗極低:Goroutine 的創建相對輕量,主要消耗少量stack空間。這意味著你可以創建大量的 goroutine 而不必擔心資源耗盡的問題。
  2. 有效的線程管理:當一個 goroutine 被阻塞時,相應的管理線程將被擱置,但運行時會將其他 goroutine 分配給這個線程,使其繼續執行其他工作。這種機制確保了線程的高效使用,避免了資源浪費。
  3. 最大線程數限制:你可以透過設定 $GOMAXPROCS 來限制系統中的線程數量,確保它們不會無節制地增加。這有助於避免系統資源的過度消耗。

限制

  1. Goroutine 數量限制:理論上,Go 語言可以創建極多的 Goroutine,但實際上,系統的可用資源(記憶體和 CPU)是有限的。因此,你需要謹慎控制 Goroutine 的數量,以避免過多的併發造成資源耗盡或性能下降。
  2. 競爭條件和死鎖:Goroutine 的併發操作需要謹慎處理共享資源,否則可能出現競爭條件(race condition)和死鎖(deadlock)。這不是直接的限制,但是在 Goroutine 的設計和使用中需要特別注意,以確保程式的正確性。

小結

相信學習Go語言的各位,也是被Goroutine強大的併發能力所吸引,Goroutine還有許多細節,今天這篇只是簡單介紹Goroutine,之後會再慢慢深入探討Goroutine的奧妙之處。

📚Reference

avatar-img
17會員
83內容數
golang
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Alan的開發者天地 的其他內容
👨‍💻簡介 當我們在宣告變數時,電腦會為該變數在記憶體中分配一個位置,然後將這個變數值儲存在這個位置上,需要讀取或修改這個變數值時,電腦是透過記憶體位置來存取這個值。 今天來簡單介紹一下go的Pointer,他的特性以及常見用法。
👨‍💻簡介 在軟體開發中,錯誤無所不在。無論是網路請求失敗、檔案不存在,還是數學計算錯誤,處理錯誤是任何開發者的日常工作,系統的穩定度基本取決於對於錯誤處理是否全面,好的錯誤處理也可以產生適當的錯誤訊息,讓 Debug 更加容易。
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
👨‍💻簡介 在Go中,假如我要判斷一個資料類型是甚麼,該怎麼做呢? Golang有一個功能叫做Type Assertions(類型斷言),它的作用就是能夠在運行時檢查我的資料類型,讓我在傳遞類型時能確保資料類型是正確的。
👨‍💻簡介 make函數在slice、map和之後會介紹到的channel的初始化中扮演著關鍵的角色。本文將會簡單介紹make函數的用法,以及在初始化不同資料結構時的差異,讓你更好地理解和利用make函數。
👨‍💻簡介 套件(Package)在Golang中扮演著組織和管理程式碼的重要角色。 package就像工具箱一樣,裡面裝滿各種不同的工具,每個工具都有特定的功能。這些工具能夠幫助你完成不同的任務,從修理家具到蓋小屋,樣樣都行。
👨‍💻簡介 當我們在宣告變數時,電腦會為該變數在記憶體中分配一個位置,然後將這個變數值儲存在這個位置上,需要讀取或修改這個變數值時,電腦是透過記憶體位置來存取這個值。 今天來簡單介紹一下go的Pointer,他的特性以及常見用法。
👨‍💻簡介 在軟體開發中,錯誤無所不在。無論是網路請求失敗、檔案不存在,還是數學計算錯誤,處理錯誤是任何開發者的日常工作,系統的穩定度基本取決於對於錯誤處理是否全面,好的錯誤處理也可以產生適當的錯誤訊息,讓 Debug 更加容易。
👨‍💻簡介 當我們在寫程式時,有時候會需要在程式結束時關閉某些資源,而defer這個關鍵字,可以讓你輕鬆的實現,下面來簡單介紹一下defer以及常用的範例。,它為程式設計師提供了一種簡單而強大的工具,用於管理資源和確保程式的正確執行。
👨‍💻簡介 在Go中,假如我要判斷一個資料類型是甚麼,該怎麼做呢? Golang有一個功能叫做Type Assertions(類型斷言),它的作用就是能夠在運行時檢查我的資料類型,讓我在傳遞類型時能確保資料類型是正確的。
👨‍💻簡介 make函數在slice、map和之後會介紹到的channel的初始化中扮演著關鍵的角色。本文將會簡單介紹make函數的用法,以及在初始化不同資料結構時的差異,讓你更好地理解和利用make函數。
👨‍💻簡介 套件(Package)在Golang中扮演著組織和管理程式碼的重要角色。 package就像工具箱一樣,裡面裝滿各種不同的工具,每個工具都有特定的功能。這些工具能夠幫助你完成不同的任務,從修理家具到蓋小屋,樣樣都行。
你可能也想看
Google News 追蹤
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
這篇內容,將會講解什麼是腳本函式,以及與腳本函式相關的知識。包括腳本的簡介、使用函式(或全域變數)的注意事項、定義全域變數、定義函式、什麼是宣告、局部變數的應用。
Thumbnail
這篇內容,將會講解什麼是函式,以及與函式相關的知識。包括函式的簡介、Runtime Function、自訂函式、Script Function 腳本函式、Method 方法。
Thumbnail
GOB Go官方有提供net/rpc的RPC套件。此套件提供GOB的編/解碼,且支援TCP或HTTP傳輸方式。它可以在伺服器端註冊多個不同類型物件。 遠端存取的要求條件 方法的類型可輸出 方法的本體可輸出 方法必須要有兩個參數是輸出或內建 方法的第二個參數是指標型 方法的返回類型為
※ 非同步概念總複習 為什麼要使用 Promise? 在 JavaScript 開發中,處理非同步操作是常見需求,涉及如文件讀寫、數據庫查詢或網路請求等耗時任務。傳統的回調方式可能導致代碼結構混亂,稱為「回調地獄」,難以維護和理解。 Promise 是解決這問題的方法。它是一個物件(objec
※ 同步概念: 單純地「由上而下」執行程式碼,而且一次只執行一件事,也就是「按順序執行,一個動作結束才能切換到下一個」。缺點是你需要「等待」事情執行完畢,才能繼續往下走。 ※ 非同步概念: 盡可能讓主要的執行程序不需要停下來等待,若遇到要等待的事情,就發起一個「非同步處理」,讓主程序繼續執行,
Thumbnail
今天來介紹python的函式 函式在python中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
原本的計畫是寫系列文,一天嗑一點,雖然完整影片約17小時,分批消化卻花了我近10天,現在回去看我第一天寫的草稿,編輯時間已經是半年前...。
Thumbnail
當你需要在 Python 中執行多個任務,但又不希望它們相互阻塞時,可以使用 threading 模組。 threading 模組允許你在單個程序中創建多個執行緒,這些執行緒可以同時運行,從而實現並行執行多個任務的效果。
Thumbnail
關於多執行緒/多行程的使用方式 在Python 3.2版本之後加入了「concurrent.futures」啟動平行任務, 它可以更好的讓我們管理多執行緒/多行程的應用場景,讓我們在面對這種併發問題時可以不必害怕, 用一個非常簡單的方式就能夠處裡, 底下我們將為您展示一段程式碼: imp
Thumbnail
隨著理財資訊的普及,越來越多台灣人不再將資產侷限於台股,而是將視野拓展到國際市場。特別是美國市場,其豐富的理財選擇,讓不少人開始思考將資金配置於海外市場的可能性。 然而,要參與美國市場並不只是盲目跟隨標的這麼簡單,而是需要策略和方式,尤其對新手而言,除了選股以外還會遇到語言、開戶流程、Ap
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
這篇內容,將會講解什麼是腳本函式,以及與腳本函式相關的知識。包括腳本的簡介、使用函式(或全域變數)的注意事項、定義全域變數、定義函式、什麼是宣告、局部變數的應用。
Thumbnail
這篇內容,將會講解什麼是函式,以及與函式相關的知識。包括函式的簡介、Runtime Function、自訂函式、Script Function 腳本函式、Method 方法。
Thumbnail
GOB Go官方有提供net/rpc的RPC套件。此套件提供GOB的編/解碼,且支援TCP或HTTP傳輸方式。它可以在伺服器端註冊多個不同類型物件。 遠端存取的要求條件 方法的類型可輸出 方法的本體可輸出 方法必須要有兩個參數是輸出或內建 方法的第二個參數是指標型 方法的返回類型為
※ 非同步概念總複習 為什麼要使用 Promise? 在 JavaScript 開發中,處理非同步操作是常見需求,涉及如文件讀寫、數據庫查詢或網路請求等耗時任務。傳統的回調方式可能導致代碼結構混亂,稱為「回調地獄」,難以維護和理解。 Promise 是解決這問題的方法。它是一個物件(objec
※ 同步概念: 單純地「由上而下」執行程式碼,而且一次只執行一件事,也就是「按順序執行,一個動作結束才能切換到下一個」。缺點是你需要「等待」事情執行完畢,才能繼續往下走。 ※ 非同步概念: 盡可能讓主要的執行程序不需要停下來等待,若遇到要等待的事情,就發起一個「非同步處理」,讓主程序繼續執行,
Thumbnail
今天來介紹python的函式 函式在python中是非常重要的一環,因為到了後期,程式會越來越複雜。 而函式可以想成是容易管理的小程式,當我們需要使用時,只需呼叫即可。
原本的計畫是寫系列文,一天嗑一點,雖然完整影片約17小時,分批消化卻花了我近10天,現在回去看我第一天寫的草稿,編輯時間已經是半年前...。
Thumbnail
當你需要在 Python 中執行多個任務,但又不希望它們相互阻塞時,可以使用 threading 模組。 threading 模組允許你在單個程序中創建多個執行緒,這些執行緒可以同時運行,從而實現並行執行多個任務的效果。
Thumbnail
關於多執行緒/多行程的使用方式 在Python 3.2版本之後加入了「concurrent.futures」啟動平行任務, 它可以更好的讓我們管理多執行緒/多行程的應用場景,讓我們在面對這種併發問題時可以不必害怕, 用一個非常簡單的方式就能夠處裡, 底下我們將為您展示一段程式碼: imp