当前位置: 首页 > 知识库问答 >
问题:

go语言为什么channel的close和接收需要放在不同的协程,且close不能放在主goroutine中,否则会报错all goroutines are asleep - deadlock?

燕靖
2023-05-05

以下的代码会报错:

package main

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

func worker(id int, sem chan struct{}, wg *sync.WaitGroup) int {
    defer wg.Done()

    // 请求一个资源

    // 模拟耗时操作
    fmt.Printf("Worker %d started\n", id)
    time.Sleep(2 * time.Second)
    fmt.Printf("Worker %d finished\n", id)

    // 释放一个资源
    // <-sem
    return 1
}

func main() {
    // 定义最大并发数
    const maxConcurrency = 3

    // 创建带缓冲的通道,用于限制并发数
    sem := make(chan struct{}, maxConcurrency)
    result := make(chan int)

    // 使用 sync.WaitGroup 等待所有 goroutine 完成
    var wg sync.WaitGroup

    // 启动 10 个 worker
    for i := 1; i <= 10; i++ {
        wg.Add(1)
        go func(i int) {
            sem <- struct{}{}
            res := worker(i, sem, &wg)
            result <- res
            <-sem
        }(i)
    }

    // 等待所有 worker 完成
    // go func() {

    // }()
    wg.Wait()
    close(result)

    for i := range result {
        fmt.Printf("%v", i)
    }
}

wg.Wait()和close(result)放在主goroutine中会报错all goroutines are asleep - deadlock!。正常我的理解不是等待wg.Done,所有的协程操作完成后,这边就会wait完成,随后close channel,最后接收channel,但是会报错死锁。可是放在另外一个goroutine不也是一样的流程吗,也是wait,close,随后接收channel,有什么区别吗

共有1个答案

太叔灿
2023-05-05

1.goroutine和worker goroutines之间存在一个死锁,你可以参考一下面的修改:

package main

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

func worker(id int, sem chan struct{}, wg *sync.WaitGroup) int {
    defer wg.Done()

    sem <- struct{}{} // 请求一个资源

    fmt.Printf("Worker %d started\n", id)
    time.Sleep(2 * time.Second)
    fmt.Printf("Worker %d finished\n", id)

    <-sem // 释放资源
    return id
}

func main() {
    const maxConcurrency = 3

    sem := make(chan struct{}, maxConcurrency)
    result := make(chan int)

    var wg sync.WaitGroup

    for i := 1; i <= 10; i++ {
        wg.Add(1)
        go func(i int) {
            res := worker(i, sem, &wg)
            result <- res
        }(i)
    }

    // 用另一个 goroutine 来接收结果
    go func() {
        wg.Wait()
        close(result)
    }()

    // 从 result channel 中获取数据
    for res := range result {
        fmt.Printf("Result: %d\n", res)
    }
}
 类似资料:
  • 问题内容: 仅仅是因为动态类型,我们不需要python中的接口概念(例如Java和C#)吗? 问题答案: 将 作为关键字和神器是在Java引入1(和C#把它从那里)来描述一下合同的对象必须坚持为。 但是,接口一直是面向对象范例的关键部分,基本上它表示对象必须响应的方法。Java只是强制执行此机制以提供静态类型检查。 因此,动态(OO)编程语言 确实会 使用接口,甚至认为它们不会静态检查它们。就像其

  • 问题内容: 当我不再需要在程序中使用那些ResultSet和Connection的实例时,为什么还要对它们都调用.close()方法呢? 不这样做有什么危险(如果有)? 问题答案: 这里有两个问题: 数据库连接 保持数据库连接打开会消耗数据库上的资源。它使用内存,并且数据库被配置为具有最大数量的连接,因此您增加了连接用尽的可能性。此外,会话的状态也得到维护,因此您可能会遇到意外锁定超出其预期范围的

  • 问题内容: 我刚刚看到了Go编程语言的演示,并认为我会尝试写几行。一切正常,直到在这种情况下我尝试使用接口为止。我该如何解决? 我收到编译器错误: 我想使用一个指针,以便inc()会影响函数外的实体。我应该使用什么语法? /瑞奇 问题答案: 我认为这里有些混乱。是类型的方法,而不是类型的方法(虽然您可以直接在指针上调用值的方法;通常不能直接在值上调用指针的方法)。您可能会感到困惑的是为什么您可以打

  • 我已经发出了一个类似于上面的http请求。 为了释放基础连接,调用(1)和(2)是否正确?这两种调用之间有什么区别?

  • 来自JDBC规范 关闭语句对象将关闭该语句对象生成的任何ResultSet实例并使其无效。在垃圾收集再次运行之前,ResultSet对象持有的资源可能不会被释放,因此当不再需要ResultSet对象时,显式关闭它们是一种很好的做法。 > 调用ResultSet.close()是否立即释放ResultSet对象? 调用Statement.close()是否立即释放语句对象? 调用语句。close()

  • 我正试图从图中的窗体向表插入子层,但为什么不能使用where呢?