繁体   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