亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Go - 為什么調度 goroutine 后臺工作人員也需要自己的 goroutine?

Go - 為什么調度 goroutine 后臺工作人員也需要自己的 goroutine?

Go
哈士奇WWW 2021-07-28 20:18:13
我正在研究 Go 的一些并發模式。我查看了使用 goroutine 和輸入/輸出通道來實現后臺工作者,并注意到當我將新作業發送到接收通道(基本上是將新作業排隊)時,我必須在 goroutine 中執行它,否則調度會被搞砸。意義:這會崩潰:for _, jobData := range(dataSet) {    input <- jobData}這有效:go func() {    for _, jobData := range(dataSet) {        input <- jobData    }}()對于更具體的事情,我玩了一些無意義的代碼(這里是在 go playground 中):package mainimport (    "log"    "runtime")func doWork(data int) (result int) {    // ... some 'heavy' computation    result = data * data    return}// do the processing of the input and return// results on the output channelfunc Worker(input, output chan int) {    for data := range input {        output <- doWork(data)    }}func ScheduleWorkers() {    input, output := make(chan int), make(chan int)    for i := 0 ; i < runtime.NumCPU() ; i++ {        go Worker(input, output)    }    numJobs := 20    // THIS DOESN'T WORK    // and crashes the program    /*    for i := 0 ; i < numJobs ; i++ {        input <- i    }    */    // THIS DOES    go func() {        for i := 0 ; i < numJobs ; i++ {            input <- i        }    }()    results := []int{}    for i := 0 ; i < numJobs ; i++ {        // read off results        result := <-output        results = append(results, result)        // do stuff...    }    log.Printf("Result: %#v\n", results)}func main() {    ScheduleWorkers()}我正在努力解決這個微妙的差異 - 感謝您的幫助。謝謝。
查看完整描述

3 回答

?
智慧大石

TA貢獻1946條經驗 獲得超3個贊

您的ScheduleWorks函數在主協程(即運行該main()函數的協程,程序在該協程中啟動)中通過input. AWorker接收它,并通過 發送另一個值output。但是此時沒有人接收output,因此程序無法繼續,主 goroutine 將下一個值發送到另一個Worker.

對每個 Worker 重復這個推理。你有runtime.NumCPU()工人,這可能少于numJobs. 比方說runtime.NumCPU() == 4,所以你有 4 個工人。最后,您已成功發送 4 個值,每個值都是一對一的Worker。由于沒有人從 讀取output,所有 Worker 都忙于嘗試發送,因此它們無法通過 接受更多數據input,因此第五個input <- i將掛起。此時每個 goroutine 都在等待;這就是僵局。


http://img1.sycdn.imooc.com//6107a234000154b405730256.jpg

您會注意到,如果您啟動 20 個或更多的 Worker 而不是 runtime.NumCPU(),則該程序可以運行。那是因為主 goroutine 可以通過 發送它想要的所有內容input,因為有足夠的工作人員來接收它們。

如果不是所有這些,而是將input <- i循環放在另一個 goroutine 中,就像在您成功的示例中一樣,maingoroutine(在其中ScheduleWorks運行)可以繼續并從output. 所以,每次這個新的 goroutine 發送一個值時,worker 發送另一個值,output主 goroutine 得到這個輸出,worker 可以接收另一個值。沒有人等待,程序成功了。

http://img1.sycdn.imooc.com//6107a24400013af105760254.jpg

查看完整回答
反對 回復 2021-08-02
?
有只小跳蛙

TA貢獻1824條經驗 獲得超8個贊

這是因為 Go 中的所有內容默認都是阻塞的。

當您在無緩沖通道上發送第一個值時,它會阻塞,直到接收器從通道中取出該值。

可以通過添加“容量”來緩沖通道。

例如:

make(chan int, 20) // Make a buffered channel of int with capacity 20

Go 規范

容量(以元素數為單位)設置通道中緩沖區的大小。如果容量大于零,則通道是異步的:如果緩沖區未滿(發送)或非空(接收),則通信操作成功而不會阻塞,并且元素按發送順序接收。如果容量為零或不存在,則只有當發送方和接收方都準備好時,通信才能成功。

您可以通過使用緩沖通道而不是非緩沖通道來使原始函數工作,但是將函數調用包裝在 goroutine 中可能是更好的方法,因為它實際上是并發的。

來自Effective Go(完整閱讀此文檔!它可能是 Stack Overflow 上 Go 答案中鏈接最多的文檔):

接收器總是阻塞直到有數據要接收。如果通道未緩沖,則發送方將阻塞,直到接收方收到該值。如果通道有緩沖區,則發送方只會阻塞,直到值被復制到緩沖區;如果緩沖區已滿,這意味著等待某個接收器檢索到一個值。

如果您使用緩沖通道,那么您只是填充通道,繼續前進,然后再次排空。不能同時進行。

例子:

改變

input, output := make(chan int), make(chan int)

input, output := make(chan int, 20), make(chan int, 20)


查看完整回答
反對 回復 2021-08-02
  • 3 回答
  • 0 關注
  • 261 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號