从通道读取时未检测到“死锁”

Posted

技术标签:

【中文标题】从通道读取时未检测到“死锁”【英文标题】:Undetected "deadlock" while reading from channel 【发布时间】:2016-11-14 03:12:14 【问题描述】:

从复杂程序的通道中读取不确定数量任务的执行结果时出现未检测到死锁的情况如何处理,例如:网络服务器?

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() 
    rand.Seed(time.Now().UTC().UnixNano())

    results := make(chan int, 100)

    // we can't know how many tasks there will be
    for i := 0; i < rand.Intn(1<<8)+1<<8; i++ 
        go func(i int) 
            time.Sleep(time.Second)
            results <- i
        (i)
    

    // can't close channel here 
    // because it is still written in
    //close(results)

    // something else is going on other threads (think web server)
    // therefore a deadlock won't be detected
    go func() 
        for 
            time.Sleep(time.Second)
        
    ()

    for j := range results 
        fmt.Println(j)
        // we just stuck in here
    

如果程序更简单,请转到detects a deadlock and properly fails。大多数示例要么获取已知数量的结果,要么按顺序写入通道。

【问题讨论】:

这实际上不是一个死锁——你没有两个线程争用一个资源,你有一个线程在等待永远不会到来的数据。不涉及锁定。 在 go 错误消息中,一个非常相似的情况需要死锁:throw: all goroutines are asleep - deadlock!;但我明白你的意思 【参考方案1】:

诀窍是使用sync.WaitGroup 并等待任务以非阻塞方式完成。

var wg sync.WaitGroup

// we can't know how many tasks there will be
for i := 0; i < rand.Intn(1<<8)+1<<8; i++ 
    wg.Add(1)
    go func(i int) 
        time.Sleep(time.Second)
        results <- i
        wg.Done()
    (i)


// wait for all tasks to finish in other thread
go func() 
    wg.Wait()
    close(results)
()

// execution continues here so you can print results

另请参阅:Go Concurrency Patterns: Pipelines and cancellation - The Go Blog

【讨论】:

以上是关于从通道读取时未检测到“死锁”的主要内容,如果未能解决你的问题,请参考以下文章

使用通道作为队列的死锁

使用带有选择的通道时的 Goroutine 死锁

为啥在使用等待组和通道时会出现死锁?

为啥在全局范围内声明通道会产生死锁问题

范围通道以死锁结束

从 goroutine 通道读取而不阻塞