【问题标题】:All goroutines are sleep deadlock所有的 goroutine 都是休眠死锁
【发布时间】:2021-06-30 12:40:01
【问题描述】:

模拟我的真正问题我有这个代码。
基本上,数组“字母”的每个元素及其索引都被发送到一个 goroutine 以将其与“x”进行比较,然后它通过通道发送响应。 我的想法是它在“x”线程上运行,在实际情况下我使用 8 个线程。

package main

import (
    "strconv"
    "sync"
)

var wg sync.WaitGroup
const sizeLetters = 12

func detectX(ch2 chan int, j int, letters [sizeLetters]string) {
    if letters[j] == "x" {
        ch2 <- j
    }else{
        ch2 <- -1
    }
}


func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    letters := [sizeLetters]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"}
    threads:= 4
    wg.Add(threads)
    for i := 0; i < threads; i++ {
        go func() {
            for {
                j, ok := <-ch1
                if !ok {
                    wg.Done()
                }
                detectX(ch2, j, letters)
            }
        }()
    }
    for i := 0; i < sizeLetters; i++ {
        ch1<-i // add i to the queue
    }
    k, ok := <-ch2 //k contains the position of X, if exist
    if !ok {
        wg.Done()
    }
    if k != -1 { //when exist
        println("X exist in position: " + strconv.Itoa(k))
    }
    println("X doesn´t exist")
    close(ch2)
    close(ch1)
    wg.Wait()
}

【问题讨论】:

  • make(chan int) 创建一个阻塞通道。只有在与并发读取匹配时,对它的写入才会完成。当detectX() 首次写入ch2 时,没有人从中读取,因为main() 仍在尝试将值推送到ch1。您还尝试将 12 个值写入 ch2,但只尝试读取一次。
  • @Zyl 我必须在我的代码中更正它,也许是在上下文中,但我仍然不知道如何很好地实现它。当检测到或未检测到 X 时,您将如何从 detectX 发送响应。
  • context 存在于取消信号中,因此在这里有意义:您希望在找到结果后停止工作。另外,除非您确实有结果,否则我不会写入 ch2 。我也会使 ch2 缓冲,即使用make(chan int, 1)。我还会通过defer 调用wg.Done()。现在你打电话给detectX(),即使你没有从ch1得到任何价值。然后 wg.Wait() 在完成写入 ch1 之后,而不是在 main() 的末尾。也许尝试一些简单的练习会有所帮助。

标签: go deadlock goroutine


【解决方案1】:

信誉不足,无法发表评论。因此,代替注释,这里是代码的替代版本:

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

const sizeLetters = 12

func detectX(ch2 chan int, j int, letters [sizeLetters]string) {
    if letters[j] == "x" {
        ch2 <- j
    }
}

func main() {
    ch1 := make(chan int)
    ch2 := make(chan int)
    letters := [sizeLetters]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"}
    threads := 4
    wg.Add(threads)
    for i := 0; i < threads; i++ {
        go func() {
            for {
                j, ok := <-ch1
                if !ok {
                    wg.Done()
                    return
                }
                detectX(ch2, j, letters)
            }
        }()
    }
    // Use a goroutine to close ch2. It is only safe to do this
    // after all the other goroutines have exited.
    go func() {
        wg.Wait()
        close(ch2)
    }()
    for i := 0; i < sizeLetters; i++ {
        ch1 <- i // add i to the queue
    }
    close(ch1)
    if k, ok := <-ch2; ok && k != -1 { //when exist
        fmt.Println("X exist in position:", k)
    } else {
        fmt.Println("X doesn´t exist")
    }
}

它仍然存在一些与数据相关的问题(除非保证letters 数组不包含重复项):

  • 即如果数组中有多个"x",goroutines 不会全部退出。也就是说,main() 不会耗尽 ch2
  • 如果有多达threads "x" 的值,那么代码将在main() 中的顶级for 循环中死锁,因为对ch1 的写入将耗尽未阻塞的goroutine 来消耗它们.
    • 如果您知道letters 数组中可能有多少个"x" 值,则可以将ch2 通道设置得那么深:ch2 := make(chan int, depth)。这将允许所有 goroutine 退出,但 ch2 可能仍会包含未排水的数据。

【讨论】:

  • 感谢您的帮助,实际上“detectX”函数代表一个包含许多验证的函数,当一个验证检测到错误时,它通过ch2通道报告它(在示例中表示为ch2
猜你喜欢
  • 1970-01-01
  • 2014-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-22
  • 2018-05-28
  • 2012-09-06
相关资源
最近更新 更多