簡體   English   中英

在 golang 中睡眠會阻塞其他 goroutine 嗎?

[英]does Sleep in golang block other goroutine?

其實我正在嘗試做這樣的事情:

我有一個生產者和一個消費者,消費者每隔幾分鍾檢查一次並計算頻道中的事件。 但是當我嘗試在 go 操場上測試它時,我發現:

package main

import (
    "fmt"
    "time"
)

func main() {

    c1 := make(chan string, 1)
    quit := make(chan int)
    go func() {
        for i := 0; i < 10; i++ {

            c1 <- "result 1"
        }
        quit <- 1
    }()

    count := 0
    for stop := false; !stop; {
        for bk := false; !bk; {
            select {
            case _, ok := <-c1:
                if ok {
                    count++
                }
            default:
                bk = true
                fmt.Println("default")
            }
        }
        select {
        case <-quit:
            fmt.Println("stop")
            stop = true
        default:
        }
        fmt.Println(count)
        time.Sleep(time.Second / 10)

    }
    fmt.Println("over")

}

無論我睡多久,從 time.Second/10 到 time.Second*10,output 將是:

default
0
default
2
default
4
default
6
default
8
default
10
default
stop
10
over

為什么 goroutine 只能在通道中放置 2 個事件? 我想要類似的東西:

default
0
default
stop
10
over

問題是頻道大小,我只是從其他代碼中復制而不檢查......

這個 function:

go func() {
    for i := 0; i < 10; i++ {

        c1 <- "result 1"
    }
    quit <- 1
}()

總是——嗯,總是在它可以運行的時候——試圖將一個字符串放入通道c1 (直到它已經放入十個,無論如何)。

制作了頻道c1

c1 := make(chan string, 1)

所以它有空間容納一個待處理的項目。 因此,如果通道為空,則循環放入一個項目,然后嘗試放入第二個項目。 如果此時通道已滿——不能保證它是滿的,但暫時假設它是滿的——這個 goroutine 現在暫停,等待有人將前一個項目從通道中拉出。

同時,在加或減幾納秒的時間——或者可能在其他 goroutine 塊1之前或之后——你正在運行另一段代碼。 (不能保證確實如此,但事實就是如此。)

    for bk := false; !bk; {
        select {
        case _, ok := <-c1:
            if ok {
                count++
            }
        default:
            bk = true
            fmt.Println("default")
        }
    }

此代碼檢查通道中是否有任何內容。 因為匿名發件人將一件物品放入其中,所以頻道中確實有一些東西。 此代碼刪除一個項目,在通道中創建空間。 這會導致匿名發件人中的阻止發送現在運行一步。 不能保證它會這樣做,但事實上,它確實會這樣做 - 所以現在頻道中有另一個項目。

但是,在運行了這一步之后,匿名發件人現在會暫停幾納秒。 2您的循環返回頂部並檢查c1中是否有項目。 有,所以你的循環接受它並計算它:你現在已經接受了另外兩個項目。 您的循環返回頂部並檢查c1中是否還有其他項目。 匿名發件人仍在喘氣,或者類似的事情,並且沒有在通道中獲得第三個值 - 因此您的循環檢測到通道是空的並采用default子句,這會在此處中斷您的循環。

運行main的 goroutine 現在打印行default ,檢查它是否應該停止(否),並暫停幾分之一秒。 在所有這些過程中的某個時間——實際上,在暫停時——匿名發送者有機會運行,將一個項目放入通道,並在它試圖將第二個項目放入通道的點處阻塞。 這只需要幾十或幾百納秒。

Sleep呼叫仍被阻止,您的匿名發件人也是如此,但喚醒將在幾分之一秒內發生。 當它這樣做時,您的主循環將返回頂部,以運行從c1中讀取項目的內部!bk循環。 您現在位於與上次相同的 state 中,因此您這次也將從c1中讀取兩個項目。

這對程序運行的 rest 重復。

這里的幾個步驟不能保證以這種方式發生。 考慮到當前的實現以及您在單 CPU 虛擬機上運行所有這些的事實,它們只是碰巧實際上是這樣運行的。 如果這兩種情況中的任何一種發生變化——例如,如果你在多 CPU 系統上運行,或者實現被修改——你的程序的行為可能會改變。


1事實上,第一次通過時,主程序正在運行,匿名發件人尚未啟動,因此計數為零。 主 goroutine 在其Sleep調用中阻塞,這允許匿名發送者在單個 CPU 上運行,為您第二次通過主例程做好准備。

2或者,在這種情況下,只要主 goroutine 有機會再次運行。 這具體取決於 Playground VM 的單 CPU 方面。

[D] oes 在 golang 中休眠會阻塞其他 goroutine?

不。

    for stop := false; !stop; {

        for bk := false; !bk; {
            select {
            case _, ok := <-c1:
                // --------- add this ---------
                time.Sleep(time.Second / 100)
                if ok {
                    count++
                }
            default:
                bk = true
                fmt.Println("default")
            }
        }

        select {
        case <-quit:
            fmt.Println("stop")
            stop = true
        default:
        }
        fmt.Println(count)
        time.Sleep(time.Second / 10)

    }
    fmt.Println("over")

因為通道比“選擇默認”慢。

暫無
暫無

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

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