Golang使用等待组的工作程序

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

Golang worker using wait groups

问题

我是你的中文翻译助手,以下是你提供的代码的翻译:

我是Golang的新手,正在尝试理解WaitGroups和Golang中的并发。在这个例子中,创建了5个工作线程,并使用一个通道将作业传递给每个工作线程。工作线程被设置为休眠1秒钟,以模拟繁重的计算。一切都进行得很顺利,但程序无法正常退出,而是打印出以下错误信息。请帮助理解为什么会发生这种情况。

fatal error: all goroutines are asleep - deadlock!

以下是代码:


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

func worker(wg *sync.WaitGroup, messageChannel <-chan string) {
	defer wg.Done()
	for i := range messageChannel {
		time.Sleep(time.Second)
		fmt.Println("done processing - ", i)
	}
}

func stop(wg *sync.WaitGroup) {
	fmt.Println("waiting on the main thread")
	wg.Wait()
}

func main() {
	wg := new(sync.WaitGroup)
	messageChannel := make(chan string, 50)

	// 创建工作线程
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go worker(wg, messageChannel)
	}

	// 输入一些消息
	for i := 0; i < 10; i++ {
		messageChannel <- fmt.Sprint(i)
	}

	stop(wg)
	close(messageChannel)
}

提前感谢!

英文:

I'm new to Golang and trying to understand how WaitGroups and concurrency in Golang works. In this example, 5 workers are created with a channel for passing a job to each worker. The workers are made to sleep for 1 second to simulate a heavy computation. All goes well, but the program does not quit gracefully. Instead this error message is printed. Please help understand why this happens.

fatal error: all goroutines are asleep - deadlock!

This is the code,


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

func worker(wg *sync.WaitGroup, messageChannel &lt;-chan string) {
	defer wg.Done()
	for i := range messageChannel {
		time.Sleep(time.Second)
		fmt.Println(&quot;done processing - &quot;, i)
	}
}

func stop(wg *sync.WaitGroup) {
	fmt.Println(&quot;waiting on the main thread&quot;)
	wg.Wait()
}

func main() {
	wg := new(sync.WaitGroup)
	messageChannel := make(chan string, 50)

	// create workers
	for i := 0; i &lt; 5; i++ {
		wg.Add(1)
		go worker(wg, messageChannel)
	}

	// input some messages
	for i := 0; i &lt; 10; i++ {
		messageChannel &lt;- fmt.Sprint(i)
	}

	stop(wg)
	close(messageChannel)
}

Thanks in advance!

答案1

得分: 3

稍微解释一下@Peter的评论,这是你编写的代码的执行流程:

  • 初始化后,你启动了worker goroutine。每个worker goroutine都会遍历messageChannel,并且每隔1秒打印一条消息。
  • 接下来,你通过for循环向messageChannel插入一些消息。每个可用的worker goroutine都会接收一条消息,直到所有消息都被处理并打印出来。之后,worker goroutine会等待messageChannel中的新消息到来。
  • 在完成向messageChannel插入消息的for循环后,你执行了stop函数,该函数在wg.Wait()上阻塞,并等待所有worker goroutine中的wg.Done()调用执行。然而,由于messageChannel没有关闭,没有一个worker goroutine能够完成执行,也没有一个wg.Done()调用被执行。
  • worker goroutine被阻塞,因为messageChannel从未关闭,main goroutine被stop函数内的wg.Wait()调用阻塞,最终导致所有goroutine都处于休眠状态的死锁。

根据建议,你只需要交换stopclose的调用顺序:

//代码的其余部分
close(messageChannel)
stop(wg)

这样,当所有消息都插入到messageChannel中后,你调用close(messageChannel),然后调用stop(wg),该调用在wg.Wait上阻塞。close(messageChannel)调用确保一旦所有消息从messageChannel中读取完毕,worker goroutine中的for-range循环将退出,并且所有的defer wg.Done()调用将被执行。一旦发生这种情况,wg.Wait()将解除阻塞,程序将正常结束执行。

英文:

To expand a bit on @Peter's comment, here is the execution flow of the code that you wrote:

  • After initialization, you start your worker goroutines. Each worker goroutine will range over messageChannel, with a time delay of 1 second will print out a message.
  • Next, you insert some message in messageChannel through a for-loop. Each available worker goroutine receives a message until all messages are processed and printed out. After that, the worker goroutines are waiting for new messages to come from the messageChannel.
  • After your for-loop for inserting messages in the messageChannel is completed, you execute the stop function, which blocks on wg.Wait() and waits for all wg.Done() calls to be executed in all worker goroutines. However, since messageChannel is not closed, none of the worker goroutines can finish execution and none of the wg.Done() calls are executed.
  • The worker goroutines are stuck because the messageChannel never closes, the main goroutine is stuck because of the wg.Wait() call inside the stop function, and you end up with a deadlock where all goroutines are asleep.

Per suggestion, you just need to swap places for stop and close calls

//rest of the code
close(messageChannel)
stop(wg)

This way, when all messages are inserted in the messageChannel, you call close(messageChannel) and then stop(wg), which blocks on the wg.Wait call. The close(messageChannel) call ensures that, once all messages are read from the messageChannel, for-range loops on the messageChannel inside the worker goroutines will exit, and all defer wg.Done() calls will be executed. Once this happens, wg.Wait() will unblock and the program will finish execution properly.

huangapple
  • 本文由 发表于 2021年10月28日 03:59:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/69744756.html
匿名

发表评论

匿名网友

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

确定