Golang 并發(fā)編程入門Goroutine 簡(jiǎn)介與基礎(chǔ)用法小結(jié)
一、什么是 Goroutine?
Goroutine 是 Golang 中的一種輕量級(jí)線程,用于實(shí)現(xiàn)并發(fā)操作。與傳統(tǒng)線程相比,Goroutine 的優(yōu)勢(shì)在于它具有更低的資源消耗和更高的效率。每個(gè) Goroutine 在運(yùn)行時(shí)被 Go 的調(diào)度器(Scheduler)管理,并且它們共享內(nèi)存空間。這使得在單個(gè)系統(tǒng)線程上運(yùn)行成千上萬個(gè) Goroutine 成為可能。
1、Goroutine 的特點(diǎn):
- 輕量級(jí):內(nèi)存開銷非常小,大約只需 2KB。
- 非搶占式調(diào)度:Goroutine 自愿讓出 CPU,通過協(xié)作完成任務(wù)切換。
- 并發(fā)與并行:Goroutine 提供并發(fā)模型,多個(gè)任務(wù)可以同時(shí)進(jìn)行。
- 阻塞模型簡(jiǎn)化開發(fā):避免了復(fù)雜的回調(diào)函數(shù),使代碼結(jié)構(gòu)更加簡(jiǎn)潔。
二、如何啟動(dòng) Goroutine
要啟動(dòng) Goroutine,我們只需要在函數(shù)調(diào)用前加上 go
關(guān)鍵字。此操作會(huì)將該函數(shù)在單獨(dú)的 Goroutine 中異步執(zhí)行,主程序不會(huì)等待它完成。
語法:
go 函數(shù)名(參數(shù))
示例 1:基本的 Goroutine 用法
package main import ( "fmt" "time" ) func printMessage() { fmt.Println("Goroutine is running!") } func main() { go printMessage() // 啟動(dòng) Goroutine time.Sleep(1 * time.Second) // 暫停主線程,確保 Goroutine 執(zhí)行完 fmt.Println("Main function is done.") }
輸出:
Goroutine is running!
Main function is done.
解釋:
- 主 Goroutine(即
main
函數(shù))立即啟動(dòng)了一個(gè)子 Goroutine 來運(yùn)行printMessage
函數(shù)。 - 如果沒有
time.Sleep
,主 Goroutine 可能會(huì)在子 Goroutine 還未執(zhí)行時(shí)結(jié)束,導(dǎo)致沒有輸出。
三、主 Goroutine 與子 Goroutine 的關(guān)系
- 主 Goroutine:
main
函數(shù)所在的 Goroutine,程序從這里開始運(yùn)行。 - 子 Goroutine:由主 Goroutine 或其他 Goroutine 創(chuàng)建的 Goroutine。
關(guān)鍵點(diǎn):如果主 Goroutine 結(jié)束,所有未完成的子 Goroutine 也會(huì)被強(qiáng)制終止。
示例 2:主線程結(jié)束導(dǎo)致子 Goroutine 終止
package main import ( "fmt" "time" ) func main() { go func() { time.Sleep(2 * time.Second) fmt.Println("This message may never appear.") }() fmt.Println("Main function is done.") // 主 Goroutine 立即結(jié)束,子 Goroutine 沒有機(jī)會(huì)完成 }
輸出:
Main function is done.
解釋:
主 Goroutine 結(jié)束時(shí),所有未完成的子 Goroutine 會(huì)立即停止,因此不會(huì)看到子 Goroutine 的輸出。
四、Goroutine 的參數(shù)傳遞
在 Golang 中,Goroutine 支持傳遞參數(shù),但需要注意是按值傳遞,而不是按引用。
示例 3:Goroutine 傳遞參數(shù)
package main import ( "fmt" ) func printNumber(num int) { fmt.Println("Number:", num) } func main() { for i := 1; i <= 3; i++ { go printNumber(i) // 啟動(dòng) Goroutine 傳遞參數(shù) } fmt.Scanln() // 等待用戶輸入,防止程序提前結(jié)束 }
輸出(可能):
Number: 1
Number: 2
Number: 3
五、匿名函數(shù)中的變量捕獲問題
Goroutine 常與匿名函數(shù)一起使用,但要小心變量捕獲問題。在循環(huán)中啟動(dòng) Goroutine 時(shí),匿名函數(shù)可能捕獲的不是當(dāng)前迭代變量的值,而是循環(huán)結(jié)束后的變量。
示例 4:錯(cuò)誤的變量捕獲
package main import ( "fmt" "time" ) func main() { for i := 1; i <= 3; i++ { go func() { fmt.Println(i) // 捕獲的可能是循環(huán)結(jié)束后的 i }() } time.Sleep(1 * time.Second) }
輸出:
4
4
4
修改后的代碼:將變量作為參數(shù)傳遞
go func(n int) { fmt.Println(n) }(i)
六、使用 WaitGroup 等待 Goroutine 完成
time.Sleep
不是等待 Goroutine 完成的最佳方式。Go 提供了 sync.WaitGroup 來管理多個(gè) Goroutine 的同步。
示例 5:使用 WaitGroup
package main import ( "fmt" "sync" ) func printMessage(msg string, wg *sync.WaitGroup) { defer wg.Done() // Goroutine 完成時(shí)調(diào)用 Done() fmt.Println(msg) } func main() { var wg sync.WaitGroup wg.Add(3) // 等待 3 個(gè) Goroutine go printMessage("Hello", &wg) go printMessage("from", &wg) go printMessage("Goroutine!", &wg) wg.Wait() // 等待所有 Goroutine 完成 fmt.Println("All Goroutines are done.") }
輸出:
Hello
from
Goroutine!
All Goroutines are done.
解釋:
wg.Add(n)
表示需要等待 n 個(gè) Goroutine 完成。- 每個(gè) Goroutine 結(jié)束時(shí)調(diào)用
wg.Done()
。 wg.Wait()
會(huì)阻塞主 Goroutine,直到所有任務(wù)完成。
七、Goroutine 的典型應(yīng)用場(chǎng)景
- I/O 并發(fā):處理大量網(wǎng)絡(luò)請(qǐng)求,減少響應(yīng)時(shí)間。
- 后臺(tái)任務(wù):執(zhí)行定時(shí)任務(wù)、日志記錄等。
- 并行計(jì)算:分布計(jì)算任務(wù),提高程序性能。
- 定時(shí)器與延時(shí)操作:如每隔一段時(shí)間執(zhí)行某個(gè)操作。
八、注意事項(xiàng)與最佳實(shí)踐
- 避免死鎖:當(dāng) Goroutine 等待彼此時(shí)可能出現(xiàn)死鎖,應(yīng)小心處理共享資源。
- 合理使用 WaitGroup 和 Channel:確保 Goroutine 的同步與通信。
- 避免啟動(dòng)過多 Goroutine:雖然 Goroutine 輕量,但過多的 Goroutine 也會(huì)占用系統(tǒng)資源。
- 調(diào)試工具:使用
go tool trace
和pprof
進(jìn)行性能調(diào)優(yōu)和調(diào)試。
九、小結(jié)
Goroutine 是 Golang 的強(qiáng)大并發(fā)工具,其高效、易用的特點(diǎn)讓它成為開發(fā)者實(shí)現(xiàn)并發(fā)程序的首選。在這篇博客中,我們介紹了 Goroutine 的基礎(chǔ)用法、參數(shù)傳遞和同步機(jī)制,并演示了常見的應(yīng)用場(chǎng)景和注意事項(xiàng)。
到此這篇關(guān)于Golang 并發(fā)編程入門:Goroutine 簡(jiǎn)介與基礎(chǔ)用法的文章就介紹到這了,更多相關(guān)goland goroutine用法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言常見錯(cuò)誤之a(chǎn)ny沒傳遞任何信息解決分析
Go語言,由于其高效強(qiáng)大的并行處理能力和優(yōu)雅簡(jiǎn)單的設(shè)計(jì)哲學(xué),一直以來都是編程世界的寵兒,然而,對(duì)于一些Go新手和甚至熟悉Go的程序員也可能會(huì)遇到一個(gè)常見的錯(cuò)誤:?any沒傳遞任何信息,那么,如何規(guī)避這個(gè)錯(cuò)誤,本文將揭示其中的秘密2024-01-01golang值類型轉(zhuǎn)換成[]uint8類型的操作
這篇文章主要介紹了golang值類型轉(zhuǎn)換成[]uint8類型的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-05-05GO語言實(shí)現(xiàn)的http抓包分析工具pproxy介紹
這篇文章主要介紹了GO語言實(shí)現(xiàn)的http抓包分析工具pproxy介紹,本文同時(shí)對(duì)比了Fiddler、Charles等抓包軟件,需要的朋友可以參考下2015-03-03go語言中數(shù)據(jù)接口set集合的實(shí)現(xiàn)
set集合是一種常見的數(shù)據(jù)結(jié)構(gòu),它代表了一個(gè)唯一元素的集合,本文主要介紹了set的基本特性,包括唯一性、無序性、可變性和集合運(yùn)算,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-10-10教你用go語言實(shí)現(xiàn)比特幣交易功能(Transaction)
每一筆比特幣交易都會(huì)創(chuàng)造輸出,輸出都會(huì)被區(qū)塊鏈記錄下來。給某個(gè)人發(fā)送比特幣,實(shí)際上意味著創(chuàng)造新的 UTXO 并注冊(cè)到那個(gè)人的地址,可以為他所用,今天通過本文給大家分享go語言實(shí)現(xiàn)比特幣交易功能,一起看看吧2021-05-05Golang創(chuàng)建第一個(gè)web項(xiàng)目(Gin+Gorm)
本文主要介紹了Golang創(chuàng)建第一個(gè)web項(xiàng)目(Gin+Gorm),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06golang結(jié)構(gòu)化日志slog的用法簡(jiǎn)介
日志是任何軟件的重要組成部分,Go?提供了一個(gè)內(nèi)置日志包(slog),在本文中,小編將簡(jiǎn)單介紹一下slog包的功能以及如何在?Go?應(yīng)用程序中使用它,感興趣的可以了解下2023-09-09詳解Golang如何使用Debug庫(kù)優(yōu)化代碼
這篇文章將針對(duì)Golang的debug庫(kù)進(jìn)行全面解讀,涵蓋其核心組件、高級(jí)功能和實(shí)戰(zhàn)技巧,文中的示例代碼講解詳細(xì),有需要的小伙伴可以參考下2024-02-02