为了账号安全,请及时绑定邮箱和手机立即绑定

退出在无缓冲通道上等待的多个 go 例程

退出在无缓冲通道上等待的多个 go 例程

Go
泛舟湖上清波郎朗 2022-10-04 19:31:56
我试图同时退出多个戈鲁廷。根据 https://www.godesignpatterns.com/2014/04/exiting-multiple-goroutines-simultaneously.html 有一个明确的方法来做到这一点。我看到的另一种方法是以下package mainimport (    "fmt"    "time")func main() {    var inCh chan int = make(chan int, 100)    var exit chan bool = make(chan bool)    for i := 0; i < 20; i++ {        go func(instance int) {            fmt.Println("In go routine ", instance)            for {                select {                case <-exit:                    fmt.Println("Exit received from ", instance)                    exit <- true                    return                case value := <-inCh:                    fmt.Println("Value=", value)                }            }        }(i)    }    time.Sleep(1 * time.Second)    exit <- true    <-exit   // Final exit    fmt.Println("Final exit")}但是我很困惑,我真的不明白为什么最后的无缓冲通道被执行为最后一个语句。实际上,我有20个go例程在听出口频道。随机一个人将收到它并将其发送给另一个人。为什么总是在go例程中进行接收,并且只有当所有例程都完成时,才会执行带有注释“//Final Exit”的通道接收?如果有人能给我一个解释,我将不胜感激。
查看完整描述

3 回答

?
MMTTMM

TA贡献1869条经验 获得超4个赞

用于取消,如链接文章中所示。close()

有问题的代码不能保证工作。下面是它失败的场景:

  1. 一个戈鲁丁已准备好从 接收。所有其他戈鲁丁人都在其他地方忙碌。exit

  2. main 发送的值由准备好的戈鲁廷接收。

  3. 该戈鲁廷向 接收的值发送一个值。exitmain()

其他戈鲁廷不会退出,因为没有更多的值被发送到 。请参阅此使用时间的游乐场示例。渗漏以诱导问题场景。exit

为什么总是在go例程中进行接收,并且只有当所有例程都完成时,才会执行带有注释“//Final Exit”的通道接收?

该程序的执行方式就好像通道维护着一个有序的等待戈鲁丁队列,但规范中没有任何内容可以保证该行为。即使通道具有有序队列,如果 goroutine 正在执行某些操作而不是等待从 接收,则程序也可能会遇到上述情况。exit


查看完整回答
反对 回复 2022-10-04
?
qq_笑_17

TA贡献1818条经验 获得超7个赞

如果您注意到程序的输出


In go routine  6

In go routine  0

In go routine  7

.

.

Exit received from  6

Exit received from  0

Exit received from  7

.

.

Final exit

它们以相同(或几乎相同)的顺序被调用,这些顺序与它们的启动方式相同。如果您的 Go 例程都不繁忙,则将使用第一个注册的例程。这只是运行时的实现,我不会指望这种行为。


您的最终出口是最后一个要收听的通道,因此最后使用它。


如果删除时间。循环后睡觉,您的最终出口几乎会立即被调用,并且您的大多数go例程都不会收到退出信号


输出与时间输出。睡眠(在跑步之间会非常多)


In go routine  0

Exit received from  0

In go routine  1

In go routine  2

In go routine  3

In go routine  4

In go routine  5

In go routine  6

In go routine  7

In go routine  14

In go routine  15

In go routine  16

In go routine  17

In go routine  18

In go routine  19

Final exit


查看完整回答
反对 回复 2022-10-04
?
四季花海

TA贡献1811条经验 获得超5个赞

请考虑此轻微修改。


package main


import (

    "fmt"

)


func main() {

    var exit chan int = make(chan int)

        var workers = 20

    for i := 0; i < workers; i++ {

        go func(instance int) {

            fmt.Println("In go routine ", instance)

            for {

                select {

                case i := <-exit:

                    fmt.Println("Exit", i, "received from ", instance)

                    exit <- i-1

                    return

                }

            }

        }(i)

    }

    exit <- workers

    fmt.Println("Final exit:", <-exit)

}

在这里,我做了3件事:首先,为了简洁起见,我删除了未使用的频道。其次,我消除了睡眠。第三,我将通道更改为每次通过都会递减的通道。如果我传入工作人员的数量,则除“最终”消息以外的任何值都表示工作人员被丢弃。exitint0


下面是一个运行示例:


% go run t.go

In go routine  8

In go routine  5

In go routine  0

In go routine  2

Exit 20 received from  8

Exit 19 received from  5

Final exit: 18

In go routine  13

当呼叫时,直到睡眠结束才会安排。其他哥鲁丁都有这个时间来设置他们的频道阅读器。我只能假设,因为我在任何地方都找不到它,频道读者很可能在大致的时间顺序错误中排队 - 因此,保证读者是最后一个。maintime.Sleepsleepmain


如果这是一致的行为,那肯定是不可靠的。


请参阅在一个频道上收听多个戈鲁丁,以获取有关此内容的更多想法。


查看完整回答
反对 回复 2022-10-04
  • 3 回答
  • 0 关注
  • 65 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号