使用goroutine时出现死锁

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

Deadlock while using goroutines

问题

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

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

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

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

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

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

main.go:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. type logEntry struct {
  8. lines []string
  9. created_at string
  10. line_count int
  11. }
  12. var wg sync.WaitGroup
  13. func main() {
  14. linesChan := make(chan (logEntry))
  15. done := make(chan (bool), 1)
  16. // 处理来自lines的条目
  17. go func() {
  18. for c := range linesChan {
  19. time.Sleep(100 * time.Millisecond)
  20. fmt.Printf("%v\n", c)
  21. }
  22. done <- true
  23. }()
  24. // 读取条目
  25. for i := 1; i <= 10; i++ {
  26. wg.Add(1)
  27. go func(i int, linesChan chan (logEntry)) {
  28. read(i, linesChan)
  29. }(i, linesChan)
  30. }
  31. // 等待所有文件都被读取
  32. wg.Wait()
  33. // 等待所有日志条目都被处理
  34. <-done
  35. close(done)
  36. }
  37. func read(count int, channel chan (logEntry)) {
  38. fmt.Println(count, "read")
  39. channel <- logEntry{
  40. line_count: count,
  41. }
  42. wg.Done()
  43. }

输出:

  1. 10 read
  2. 6 read
  3. 3 read
  4. 1 read
  5. 4 read
  6. 8 read
  7. 7 read
  8. 2 read
  9. 5 read
  10. 9 read
  11. {[] 10}
  12. {[] 6}
  13. {[] 3}
  14. {[] 1}
  15. {[] 4}
  16. {[] 8}
  17. {[] 7}
  18. {[] 2}
  19. {[] 5}
  20. {[] 9}
  21. fatal error: all goroutines are asleep - deadlock!
  22. goroutine 1 [chan receive]:
  23. main.main()
  24. .../main.go:44 +0x13a
  25. goroutine 18 [chan receive]:
  26. main.main.func1()
  27. .../main.go:24 +0x145
  28. created by main.main
  29. .../main.go:23 +0x9d
  30. 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:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;sync&quot;
  5. &quot;time&quot;
  6. )
  7. type logEntry struct {
  8. lines []string
  9. created_at string
  10. line_count int
  11. }
  12. var wg sync.WaitGroup
  13. func main() {
  14. linesChan := make(chan (logEntry))
  15. done := make(chan (bool), 1)
  16. // Process entries from lines
  17. go func() {
  18. for c := range linesChan {
  19. time.Sleep(100 * time.Millisecond)
  20. fmt.Printf(&quot;%v\n&quot;, c)
  21. }
  22. done &lt;- true
  23. }()
  24. // Read lines
  25. for i := 1; i &lt;= 10; i++ {
  26. wg.Add(1)
  27. go func(i int, linesChan chan (logEntry)) {
  28. read(i, linesChan)
  29. }(i, linesChan)
  30. }
  31. // Wait till all the files are read
  32. wg.Wait()
  33. // Wait till all the log entries are processed
  34. &lt;-done
  35. close(done)
  36. }
  37. func read(count int, channel chan (logEntry)) {
  38. fmt.Println(count, &quot;read&quot;)
  39. channel &lt;- logEntry{
  40. line_count: count,
  41. }
  42. wg.Done()
  43. }

Output:

  1. 10 read
  2. 6 read
  3. 3 read
  4. 1 read
  5. 4 read
  6. 8 read
  7. 7 read
  8. 2 read
  9. 5 read
  10. 9 read
  11. {[] 10}
  12. {[] 6}
  13. {[] 3}
  14. {[] 1}
  15. {[] 4}
  16. {[] 8}
  17. {[] 7}
  18. {[] 2}
  19. {[] 5}
  20. {[] 9}
  21. fatal error: all goroutines are asleep - deadlock!
  22. goroutine 1 [chan receive]:
  23. main.main()
  24. .../main.go:44 +0x13a
  25. goroutine 18 [chan receive]:
  26. main.main.func1()
  27. .../main.go:24 +0x145
  28. created by main.main
  29. .../main.go:23 +0x9d
  30. exit status 2

答案1

得分: 2

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

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

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. type logEntry struct {
  8. lines []string
  9. created_at string
  10. line_count int
  11. }
  12. var wg sync.WaitGroup
  13. func main() {
  14. linesChan := make(chan logEntry)
  15. // 处理来自lines的条目
  16. go func() {
  17. for c := range linesChan {
  18. time.Sleep(100 * time.Millisecond)
  19. fmt.Printf("%v\n", c)
  20. }
  21. }()
  22. // 读取行
  23. for i := 1; i <= 10; i++ {
  24. wg.Add(1)
  25. go func(i int, linesChan chan logEntry) {
  26. read(i, linesChan)
  27. }(i, linesChan)
  28. }
  29. // 等待所有文件都被读取完毕
  30. wg.Wait()
  31. }
  32. func read(count int, channel chan logEntry) {
  33. fmt.Println(count, "read")
  34. channel <- logEntry{
  35. line_count: count,
  36. }
  37. wg.Done()
  38. }
英文:

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.

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;sync&quot;
  5. &quot;time&quot;
  6. )
  7. type logEntry struct {
  8. lines []string
  9. created_at string
  10. line_count int
  11. }
  12. var wg sync.WaitGroup
  13. func main() {
  14. linesChan := make(chan (logEntry))
  15. // Process entries from lines
  16. go func() {
  17. for c := range linesChan {
  18. time.Sleep(100 * time.Millisecond)
  19. fmt.Printf(&quot;%v\n&quot;, c)
  20. }
  21. }()
  22. // Read lines
  23. for i := 1; i &lt;= 10; i++ {
  24. wg.Add(1)
  25. go func(i int, linesChan chan (logEntry)) {
  26. read(i, linesChan)
  27. }(i, linesChan)
  28. }
  29. // Wait till all the files are read
  30. wg.Wait()
  31. }
  32. func read(count int, channel chan (logEntry)) {
  33. fmt.Println(count, &quot;read&quot;)
  34. channel &lt;- logEntry{
  35. line_count: count,
  36. }
  37. wg.Done()
  38. }

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:

确定