关闭停止通道不会停止 goroutine。

huangapple go评论82阅读模式
英文:

Closing stop channel does not stop goroutines

问题

介绍:

我刚开始学习Go语言,并且已经学到了关于并发的课程。

我为自己设定了一个小任务,试图实现我所学到的有关关闭goroutine的知识。

问题:

如果我们关闭通道,它的case语句将始终在select语句中被选中,这是向所有goroutine广播取消信号的好方法。

下面有两个goroutine和一个永远不会被接收的quit通道。

代码在Playground上超时。

如果我在goroutine中注释掉default部分,那么quit信号就会被接收到。

问题:

我真的不明白为什么会发生这种情况,也不知道如何解决,尽管我正在尝试。
有人可以解释一下问题,并提供一些建议吗?

package main

import (
	"fmt"
	"sync"
	"time"
)

func positive_numbers(quit chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()
	i := 1
	for {
		select {
		case <-quit:
			fmt.Println("[+]Quiting...")
			return
		default:
			fmt.Printf("%v ", i)
			i++
		}
	}
}

func negative_numbers(quit chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()
	i := -1
	for {
		select {
		case <-quit:
			fmt.Println("[-]Quiting...")
			return
		default:
			fmt.Printf("%v ", i)
			i--
		}
	}
}

func main() {
	quit := make(chan struct{})

	wg := sync.WaitGroup{} // so we can wait for all goroutines to finish
	wg.Add(2)

	go positive_numbers(quit, &wg)
	go negative_numbers(quit, &wg)

	go func(quit chan struct{}) {
		defer close(quit)
		time.Sleep(1 * time.Second)
	}(quit)

	wg.Wait()
}
英文:

INTRODUCTION:

I am just starting to learn the Go language and have reached the lesson about concurrency.

I have invented a small task for myself to try to implement what I have learned about closing goroutines.

PROBLEM:

If we close channel it's case will always be selected in the select statement, which is a great way to broadcast cancelation signal to all goroutines.

Below I have 2 goroutines and a quit channel that never gets received.

Code timeouts on Playground.

If I comment out default part in the goroutines then quit signal gets received.

QUESTION:

I really do not understand why is this happening and how to fix it, although I am trying.<br/>
Can somebody please explain me what is the problem and offer some advice on how to solve it?<br/>

package main

import (
	&quot;fmt&quot;
	&quot;sync&quot;
	&quot;time&quot;
)

func positive_numbers(quit chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()
	i := 1
	for {
		select {
		case &lt;-quit:
			fmt.Println(&quot;[+]Quiting...&quot;)
			return
		default:
			fmt.Printf(&quot;%v &quot;, i)
			i++
		}
	}
}

func negative_numbers(quit chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()
	i := -1
	for {
		select {
		case &lt;-quit:
			fmt.Println(&quot;[-]Quiting...&quot;)
			return
		default:
			fmt.Printf(&quot;%v &quot;, i)
			i--
		}
	}
}

func main() {
	quit := make(chan struct{})

	wg := sync.WaitGroup{} // so we can wait for all goroutines to finish
	wg.Add(2)

	go positive_numbers(quit, &amp;wg)
	go negative_numbers(quit, &amp;wg)

	go func(quit chan struct{}) {
		defer close(quit)
		time.Sleep(1 * time.Second)
	}(quit)

	wg.Wait()
}

答案1

得分: 6

这段代码在实际环境中运行良好;只是与 playground 的“虚拟时间”不兼容。因为 positive_numbersnegative_numbers 没有阻塞,运行时无法确定在 Sleep 过期之前每个迭代的次数(基本上,打印操作不消耗模拟的墙钟时间,因此它们试图产生无限输出,在不推进墙钟时间的情况下,你会达到 playground 的 CPU 使用限制)。在真实的机器上,你只能在有限的时间内打印有限的输出,所以会得到合理的行为。

在 playground 中,如果在每次打印之后添加类似 time.Sleep(time.Millisecond) 的代码,你就会强制虚拟时钟向前移动,程序也会终止。

英文:

This code works alright in real life; it's just not compatible with the playground's "fake time" because positive_numbers and negative_numbers don't block, and the runtime can't decide how many iterations of each get to run before the Sleep expires (basically, the prints take zero emulated wallclock time, so they try to produce infinite output, and you hit the playground CPU usage limit without ever advancing the wallclock time at all). On real machines, where you can only print finite output in finite time, you get reasonable behavior.

In the playground, if you add something like time.Sleep(time.Millisecond) after each print, you force the fake clock to move forward, and the program also terminates.

huangapple
  • 本文由 发表于 2021年8月13日 15:16:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/68768216.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定