golang中的Sleep会阻塞其他goroutine吗?

实际上我正在尝试做这样的事情:


我有一个生产者和一个消费者,消费者每隔几分钟检查一次并计算通道中的事件。但是当我尝试在 go Playground 中测试它时,我发现:


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,输出都会是:


default

0

default

2

default

4

default

6

default

8

default

10

default

stop

10

over

为什么goroutine只能将2个事件放入通道中?我想要这样的东西:


default

0

default

stop

10

over

问题是通道大小,我只是从其他代码复制而不检查......


杨__羊羊
浏览 210回答 3
3回答

汪汪一只猫

这个功能:go func() {&nbsp; &nbsp; for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; c1 <- "result 1"&nbsp; &nbsp; }&nbsp; &nbsp; quit <- 1}()总是——嗯,总是在它可以运行的时候——尝试将一个字符串放入通道c1(无论如何,直到它放入了十个字符串)。您创建了频道c1:c1 := make(chan string, 1)因此它有空间容纳一件待处理的物品。因此,如果通道为空,循环将放入一项,然后尝试放入第二项。如果此时通道已满(不能保证已满,但暂时假设已满),此 Goroutine 现在暂停,等待有人将前一个项目从通道中拉出。与此同时,在加上或减去几纳秒的同时——或者可能在其他 goroutine 块1之前或之后——您正在运行另一段代码。(不能保证情况确实如此,但事实确实如此。)&nbsp; &nbsp; for bk := false; !bk; {&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case _, ok := <-c1:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count++&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bk = true&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("default")&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }此代码检查通道中是否有任何内容。因为匿名发件人将一项内容放入其中,所以该频道中确实有一些内容。此代码删除一项,在通道中创建空间。这会导致匿名发件人中被阻止的发送现在运行一步。无法保证它会这样做,但事实上,它确实会这样做 - 所以现在频道中出现了另一个项目。不过,运行完这一步后,匿名发件人现在暂停了几纳秒。2 您的循环返回到顶部并检查 中是否有项目c1。有,所以你的循环接受它并计算它:你现在已经接受了另外两项。您的循环返回到顶部并检查 中是否还有其他项目c1。匿名发件人仍在喘口气,或者类似的事情,并且还没有将第三个值输入到通道中,因此您的循环检测到通道为空并采用该子句default,这会在此处中断您的循环。现在运行的 goroutine 会main打印该行default,检查是否应该停止(否),并暂停几分之一秒。在所有这一切过程中的某个时间(实际上是在暂停时),匿名发送者有机会运行,将一个项目放入通道中,并在尝试将第二个项目放入通道中时阻塞。这只需要几十或几百纳秒。呼叫Sleep仍然被阻止,就像您的匿名发件人一样,但唤醒将在几分之一秒内发生。当它发生时,您的主循环将返回到顶部,以运行!bk从 中读取项目的内部循环c1。您现在处于与上次相同的状态,因此这次您还将阅读两项c1。程序运行的其余部分会重复此操作。这里的几个步骤不能保证以这种方式发生。考虑到当前的实现以及您在单 CPU 虚拟机上运行所有这些的事实,它们实际上只是碰巧以这种方式运行。如果这两种情况中的任何一个发生变化(例如,如果您在多 CPU 系统上运行,或者实现被修改),您的程序的行为可能会发生变化。1事实上,第一次通过时,主例程正在运行,匿名发送者尚未启动,因此计数为零。主 goroutine 会阻塞在其Sleep调用中,这允许匿名发送者在单个 CPU 上运行,为您第二次访问主例程做好准备。2或者,在本例中,只要主 Goroutine 给它再次运行的机会即可。这具体取决于 Playground VM 的单 CPU 方面。

SMILET

[D]oes sleep in golang 会阻塞其他 goroutine 吗?不。

慕无忌1623718

&nbsp; &nbsp;for stop := false; !stop; {&nbsp; &nbsp; &nbsp; &nbsp; for bk := false; !bk; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case _, ok := <-c1:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // --------- add this ---------&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Second / 100)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; count++&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bk = true&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("default")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case <-quit:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("stop")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; stop = true&nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(count)&nbsp; &nbsp; &nbsp; &nbsp; time.Sleep(time.Second / 10)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println("over")因为该通道比“选择默认”慢。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go