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

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

golang中的Sleep會阻塞其他goroutine嗎?

golang中的Sleep會阻塞其他goroutine嗎?

Go
楊__羊羊 2023-08-07 14:31:46
實際上我正在嘗試做這樣的事情:我有一個生產者和一個消費者,消費者每隔幾分鐘檢查一次并計算通道中的事件。但是當我嘗試在 go Playground 中測試它時,我發現:package mainimport (    "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,輸出都會是:default0default2default4default6default8default10defaultstop10over為什么goroutine只能將2個事件放入通道中?我想要這樣的東西:default0defaultstop10over問題是通道大小,我只是從其他代碼復制而不檢查......
查看完整描述

3 回答

?
汪汪一只貓

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

這個功能:


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,這會在此處中斷您的循環。


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


呼叫Sleep仍然被阻止,就像您的匿名發件人一樣,但喚醒將在幾分之一秒內發生。當它發生時,您的主循環將返回到頂部,以運行!bk從 中讀取項目的內部循環c1。您現在處于與上次相同的狀態,因此這次您還將閱讀兩項c1。


程序運行的其余部分會重復此操作。


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


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


2或者,在本例中,只要主 Goroutine 給它再次運行的機會即可。這具體取決于 Playground VM 的單 CPU 方面。


查看完整回答
反對 回復 2023-08-07
?
SMILET

TA貢獻1796條經驗 獲得超4個贊

[D]oes sleep in golang 會阻塞其他 goroutine 嗎?

不。


查看完整回答
反對 回復 2023-08-07
?
慕無忌1623718

TA貢獻1744條經驗 獲得超4個贊

   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")

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


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

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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