簡體   English   中英

為什么這個Go代碼會阻塞?

[英]Why is this Go code blocking?

我寫了以下程序:

package main

import (
    "fmt"
)

func processevents(list chan func()) {
    for {
        //a := <-list
        //a()
    }
}

func test() {
    fmt.Println("Ho!")
}

func main() {

    eventlist := make(chan func(), 100)

    go processevents(eventlist)

    for {
        eventlist <- test
        fmt.Println("Hey!")
    }
}

由於頻道事件列表是一個緩沖頻道,我想我應該得到輸出“嘿!”的100倍,但它只顯示一次。 我的錯誤在哪里?

更新(轉到版本1.2+)

從Go 1.2開始,調度程序的工作原理是先發制人的多任務處理 這意味着原始問題(以及下面介紹的解決方案)中的問題不再相關。

來自Go 1.2發行說明

調度程序中的搶占

在以前的版本中,永遠循環的goroutine可能會在同一個線程上餓死其他goroutine,這是GOMAXPROCS只提供一個用戶線程時的一個嚴重問題。 在Go> 1.2中,部分解決了這個問題:在進入函數時偶爾會調用調度程序。 這意味着任何包含(非內聯)函數調用的循環都可以被搶占,允許其他goroutine在同一個線程上運行。

簡短的回答

它不會阻止寫入。 它陷入了無限循環的processevents 這個循環永遠不會產生調度程序,導致所有goroutine無限期地鎖定。

如果您注釋掉對processevents的調用,您將獲得預期的結果,直到第100次寫入。 程序在此時會感到恐慌,因為沒有人從頻道中讀取內容。

另一個解決方案是在循環中調用runtime.Gosched()

答案很長

使用Go1.0.2,Go的調度程序基於協作式多任務處理原理。 這意味着它通過讓這些例程在某些條件下與調度程序交互,將CPU時間分配給在給定OS線程內運行的各種goroutine。 當在goroutine中執行某些類型的代碼時,會發生這些“交互”。 在go的情況下,這涉及進行某種I / O,系統調用或內存分配(在某些條件下)。

在空循環的情況下,不會遇到這樣的情況。 因此,只要該循環正在運行,就不允許調度程序運行其調度算法。 這因此可以防止它將CPU時間分配給等待運行的其他goroutine,並且您觀察到的結果隨之發生:您有效地創建了一個無法被調度程序檢測到或中斷的死鎖。

在Go中通常不需要空循環,並且在大多數情況下,將指示程序中的錯誤。 如果由於某種原因確實需要它,則必須通過在每次迭代中調用runtime.Gosched()來手動屈服於調度程序。

for {
    runtime.Gosched()
}

GOMAXPROCS設置為值> 1被提及作為解決方案。 雖然這將解決您觀察到的直接問題,但如果調度程序決定將循環goroutine移動到其自己的OS線程,它將有效地將問題移動到不同的OS線程。 除非您在processevents函數的開頭調用runtime.LockOSThread() ,否則無法保證這一點。 即便如此,我仍然不會依賴這種方法來成為一個好的解決方案。 只需在循環本身中調用runtime.Gosched() ,就可以解決所有問題,無論goroutine運行在哪個OS線程中。

這是另一種解決方案 - 使用range從通道讀取。 此代碼將正確地生成調度程序,並在通道關閉時正確終止。

func processevents(list chan func()) {
    for a := range list{
        a()
    }
}

好消息,自Go 1.2(2013年12月)以來,原始程序現在按預期工作。 你可以在Playground試試吧

這在Go 1.2發行說明的 “調度程序中的搶占”部分中進行了解釋:

在以前的版本中,永遠循環的goroutine可能會在同一個線程上餓死其他goroutine,這是GOMAXPROCS只提供一個用戶線程時的一個嚴重問題。 在Go 1.2中,部分解決了這個問題:在進入函數時偶爾會調用調度程序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM