本文深入探討了 Go 語言并發(fā)程序中一個(gè)有趣的現(xiàn)象:當(dāng)循環(huán)次數(shù)為奇數(shù)時(shí),程序能夠完整輸出所有數(shù)值;而當(dāng)循環(huán)次數(shù)為偶數(shù)時(shí),最后一個(gè)數(shù)值卻丟失。通過分析代碼示例和調(diào)度器行為,揭示了并發(fā)程序中非確定性的本質(zhì),并強(qiáng)調(diào)了使用同步機(jī)制確保 Goroutine 完成的重要性。文章將幫助讀者理解 Go 調(diào)度器的工作方式,并掌握編寫可靠并發(fā)程序的關(guān)鍵技巧。
在 Go 語言中,并發(fā)編程是一項(xiàng)強(qiáng)大的特性,它允許程序同時(shí)執(zhí)行多個(gè)任務(wù)。然而,并發(fā)也引入了非確定性,這意味著程序的行為可能因運(yùn)行環(huán)境、調(diào)度器的決策等因素而異。本文將通過一個(gè)具體的例子,深入探討 Go 調(diào)度器在并發(fā)程序中的行為,并解釋為何循環(huán)次數(shù)的奇偶性會(huì)影響程序的輸出結(jié)果。
問題描述
考慮以下 Go 代碼:
package main import "runtime" func main() { c2 := make(chan int) go func() { for v := range c2 { println("c2 =", v, "numof routines:", runtime.NumGoroutine()) } }() for i := 1; i <= 10001; i++ { c2 <- i // runtime.Gosched() } }
這段代碼創(chuàng)建了一個(gè) Goroutine,它從通道 c2 中接收整數(shù)并打印。主 Goroutine 向 c2 發(fā)送從 1 到 10001(或 10000)的整數(shù)。
令人驚訝的是,當(dāng)循環(huán)次數(shù)為奇數(shù)(例如 10001)時(shí),程序能夠完整輸出所有數(shù)值。但是,當(dāng)循環(huán)次數(shù)為偶數(shù)(例如 10000)時(shí),程序會(huì)丟失最后一個(gè)數(shù)值。
原因分析:調(diào)度器的非確定性
這種現(xiàn)象的根本原因是 Go 調(diào)度器的非確定性。當(dāng) main 函數(shù)返回時(shí),程序會(huì)終止,而不會(huì)等待任何 Goroutine 完成。因此,Goroutine 是否能在 main 函數(shù)返回之前完成所有工作,取決于調(diào)度器的調(diào)度策略以及一些外部因素。
循環(huán)次數(shù)的奇偶性可能只是影響調(diào)度的一個(gè)因素。當(dāng)循環(huán)次數(shù)為偶數(shù)時(shí),可能由于某種巧合,調(diào)度器在 Goroutine 處理完所有數(shù)據(jù)之前就切換回了 main Goroutine,導(dǎo)致 main 函數(shù)提前返回,從而導(dǎo)致最后一個(gè)數(shù)值丟失。
解決方案:使用同步機(jī)制
為了確保 Goroutine 在 main 函數(shù)返回之前完成所有工作,我們需要使用同步機(jī)制。Go 提供了多種同步機(jī)制,例如 sync.WaitGroup。
以下是如何使用 sync.WaitGroup 修改代碼以確保所有數(shù)值都被處理:
package main import ( "fmt" "runtime" "sync" ) func main() { c2 := make(chan int) var wg sync.WaitGroup wg.Add(1) // 增加計(jì)數(shù)器,表示有一個(gè) Goroutine 需要等待 go func() { defer wg.Done() // Goroutine 完成時(shí)減少計(jì)數(shù)器 for v := range c2 { fmt.Println("c2 =", v, "numof routines:", runtime.NumGoroutine()) } }() for i := 1; i <= 10000; i++ { c2 <- i // runtime.Gosched() } close(c2) // 關(guān)閉通道,通知 Goroutine 沒有更多數(shù)據(jù)了 wg.Wait() // 等待計(jì)數(shù)器歸零,表示所有 Goroutine 都已完成 }
在這個(gè)修改后的代碼中,我們使用 sync.WaitGroup 來等待 Goroutine 完成。
通過使用 sync.WaitGroup,我們可以確保 main 函數(shù)在所有 Goroutine 完成工作后才返回,從而避免了數(shù)據(jù)丟失的問題。
總結(jié)
Go 調(diào)度器的非確定性是并發(fā)編程中需要注意的一個(gè)重要問題。為了編寫可靠的并發(fā)程序,我們需要使用適當(dāng)?shù)耐綑C(jī)制來確保 Goroutine 在程序退出之前完成所有工作。sync.WaitGroup 是一個(gè)常用的同步機(jī)制,可以用于等待一組 Goroutine 完成。此外,使用 close 關(guān)閉channel也是通知goroutine不再有數(shù)據(jù)輸入的重要手段。理解這些概念并正確應(yīng)用它們,可以幫助我們編寫出更加健壯和可靠的 Go 并發(fā)程序。
以上就是Go 調(diào)度器奇偶行為探究:并發(fā)程序中的非確定性與同步機(jī)制的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!
每個(gè)人都需要一臺(tái)速度更快、更穩(wěn)定的 PC。隨著時(shí)間的推移,垃圾文件、舊注冊(cè)表數(shù)據(jù)和不必要的后臺(tái)進(jìn)程會(huì)占用資源并降低性能。幸運(yùn)的是,許多工具可以讓 Windows 保持平穩(wěn)運(yùn)行。
微信掃碼
關(guān)注PHP中文網(wǎng)服務(wù)號(hào)
QQ掃碼
加入技術(shù)交流群
Copyright 2014-2025 http://ipnx.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號(hào)