使用goroutine时出现死锁

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

Deadlock while using goroutines

问题

我有一个程序,它有两个功能:

  • 读取日志条目并创建logEntry对象
  • 处理每个logEntry实例

在这里,读取操作由单独的goroutine完成,而对所有读取的条目进行处理的操作由单个goroutine完成。

我使用了一个waitgroup - wg来确保在程序退出之前读取所有的日志条目,并使用一个信号通道 - done来确保日志条目的处理完成。

waitgroup按预期工作,但是当我调用<-done来确保程序在读取日志文件处理完成后退出时,它会抛出错误fatal error: all goroutines are asleep - deadlock!

有人能解释一下为什么会发生这种情况,以及如何修复上述错误吗?

main.go:

package main

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

type logEntry struct {
	lines      []string
	created_at string
	line_count int
}

var wg sync.WaitGroup

func main() {
	linesChan := make(chan (logEntry))

	done := make(chan (bool), 1)

	// 处理来自lines的条目
	go func() {
		for c := range linesChan {
			time.Sleep(100 * time.Millisecond)
			fmt.Printf("%v\n", c)
		}

		done <- true
	}()

	// 读取条目
	for i := 1; i <= 10; i++ {
		wg.Add(1)
		go func(i int, linesChan chan (logEntry)) {
			read(i, linesChan)
		}(i, linesChan)
	}

	// 等待所有文件都被读取
	wg.Wait()

	// 等待所有日志条目都被处理
	<-done
	close(done)
}

func read(count int, channel chan (logEntry)) {
	fmt.Println(count, "read")
	channel <- logEntry{
		line_count: count,
	}
	wg.Done()
}

输出:

10 read
6 read
3 read
1 read
4 read
8 read
7 read
2 read
5 read
9 read
{[]  10}
{[]  6}
{[]  3}
{[]  1}
{[]  4}
{[]  8}
{[]  7}
{[]  2}
{[]  5}
{[]  9}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
  .../main.go:44 +0x13a

goroutine 18 [chan receive]:
main.main.func1()
  .../main.go:24 +0x145
created by main.main
  .../main.go:23 +0x9d
exit status 2
英文:

I have a program which does 2 things:

  • Read log entries and create logEntry objects
  • Process each logEntry instances

Here, reading is done by separate goroutines and processing of all the read entries is done by a single goroutine.

I'm using a waitgroup - wg to ensure that all the log entries are read before the program quits and a signal channel - done to ensure the processing of the log entries are completed.

The waitgroup is working as expected, however when I call &lt;-done to ensure that the program exits only after the read log files are processed, it throws the error fatal error: all goroutines are asleep - deadlock!.

Could someone please explain why this is happening and how to fix the above error?

main.go:

package main

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

type logEntry struct {
	lines      []string
	created_at string
	line_count int
}

var wg sync.WaitGroup

func main() {
	linesChan := make(chan (logEntry))

	done := make(chan (bool), 1)

	// Process entries from lines
	go func() {
		for c := range linesChan {
			time.Sleep(100 * time.Millisecond)
			fmt.Printf(&quot;%v\n&quot;, c)
		}

		done &lt;- true
	}()

	// Read lines
	for i := 1; i &lt;= 10; i++ {
		wg.Add(1)
		go func(i int, linesChan chan (logEntry)) {
			read(i, linesChan)
		}(i, linesChan)
	}

	// Wait till all the files are read
	wg.Wait()

	// Wait till all the log entries are processed
	&lt;-done
	close(done)
}

func read(count int, channel chan (logEntry)) {
	fmt.Println(count, &quot;read&quot;)
	channel &lt;- logEntry{
		line_count: count,
	}
	wg.Done()
}

Output:

10 read
6 read
3 read
1 read
4 read
8 read
7 read
2 read
5 read
9 read
{[]  10}
{[]  6}
{[]  3}
{[]  1}
{[]  4}
{[]  8}
{[]  7}
{[]  2}
{[]  5}
{[]  9}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
  .../main.go:44 +0x13a

goroutine 18 [chan receive]:
main.main.func1()
  .../main.go:24 +0x145
created by main.main
  .../main.go:23 +0x9d
exit status 2

答案1

得分: 2

在你的代码中,你监听了linesChan通道,但没有关闭它。当所有数据传递完毕时,你需要关闭这个通道。done <- true不会被执行。

但在这里不需要使用通道进行同步,sync.WaitGroup{}就足够了。

package main

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

type logEntry struct {
	lines      []string
	created_at string
	line_count int
}

var wg sync.WaitGroup

func main() {
	linesChan := make(chan logEntry)

	// 处理来自lines的条目
	go func() {
		for c := range linesChan {
			time.Sleep(100 * time.Millisecond)
			fmt.Printf("%v\n", c)
		}
	}()

	// 读取行
	for i := 1; i <= 10; i++ {
		wg.Add(1)
		go func(i int, linesChan chan logEntry) {
			read(i, linesChan)
		}(i, linesChan)
	}

	// 等待所有文件都被读取完毕
	wg.Wait()
}

func read(count int, channel chan logEntry) {
	fmt.Println(count, "read")
	channel <- logEntry{
		line_count: count,
	}
	wg.Done()
}
英文:

in your case you are listening for linesChan, but not close it. You need to close this channel, when all data will be passed. done &lt;- true won't be executed.

But don't need channel for sync here, sync.WaitGroup{} will be enough.

package main
import (
&quot;fmt&quot;
&quot;sync&quot;
&quot;time&quot;
)
type logEntry struct {
lines      []string
created_at string
line_count int
}
var wg sync.WaitGroup
func main() {
linesChan := make(chan (logEntry))
// Process entries from lines
go func() {
for c := range linesChan {
time.Sleep(100 * time.Millisecond)
fmt.Printf(&quot;%v\n&quot;, c)
}
}()
// Read lines
for i := 1; i &lt;= 10; i++ {
wg.Add(1)
go func(i int, linesChan chan (logEntry)) {
read(i, linesChan)
}(i, linesChan)
}
// Wait till all the files are read
wg.Wait()
}
func read(count int, channel chan (logEntry)) {
fmt.Println(count, &quot;read&quot;)
channel &lt;- logEntry{
line_count: count,
}
wg.Done()
}

huangapple
  • 本文由 发表于 2022年9月19日 22:55:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/73775418.html
匿名

发表评论

匿名网友

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

确定