为了账号安全,请及时绑定邮箱和手机立即绑定

GO 语言:致命错误:所有 goroutine 都处于睡眠状态 - 死锁

GO 语言:致命错误:所有 goroutine 都处于睡眠状态 - 死锁

Go
慕盖茨4494581 2021-08-30 15:10:37
下面的代码适用于硬编码的 JSON 数据,但是当我从文件中读取 JSON 数据时不起作用。我得到fatal error: all goroutines are asleep - deadlock使用时的错误sync.WaitGroup。使用硬编码 JSON 数据的工作示例:package mainimport (    "bytes"    "fmt"    "os/exec"    "time")func connect(host string) {    cmd := exec.Command("ssh", host, "uptime")    var out bytes.Buffer    cmd.Stdout = &out    err := cmd.Run()    if err != nil {        fmt.Println(err)    }    fmt.Printf("%s: %q\n", host, out.String())    time.Sleep(time.Second * 2)    fmt.Printf("%s: DONE\n", host)}func listener(c chan string) {    for {        host := <-c        go connect(host)    }}func main() {    hosts := [2]string{"user1@111.79.154.111", "user2@111.79.190.222"}    var c chan string = make(chan string)    go listener(c)    for i := 0; i < len(hosts); i++ {        c <- hosts[i]    }    var input string    fmt.Scanln(&input)}输出:user@user-VirtualBox:~/go$ go run channel.gouser1@111.79.154.111: " 09:46:40 up 86 days, 18:16,  0 users,  load average: 5"user2@111.79.190.222: " 09:46:40 up 86 days, 17:27,  1 user,  load average: 9"user1@111.79.154.111: DONEuser2@111.79.190.222: DONE输出user@user-VirtualBox:~/go$ go run deploy.go < hosts.txt user1@111.79.154.111: " 09:46:40 up 86 days, 18:16,  0 users,  load average: 5"user2@111.79.190.222: " 09:46:40 up 86 days, 17:27,  1 user,  load average: 9"user1@111.79.154.111 : DONEuser2@111.79.190.222: DONEfatal error: all goroutines are asleep - deadlock!goroutine 1 [semacquire]:sync.runtime_Semacquire(0xc210000068)    /usr/lib/go/src/pkg/runtime/sema.goc:199 +0x30sync.(*WaitGroup).Wait(0xc210047020)    /usr/lib/go/src/pkg/sync/waitgroup.go:127 +0x14bmain.main()    /home/user/go/deploy.go:64 +0x45agoroutine 3 [chan receive]:main.listener(0xc210038060)    /home/user/go/deploy.go:28 +0x30created by main.main    /home/user/go/deploy.go:53 +0x30bexit status 2user@user-VirtualBox:~/go$主机.TXT[   {      "username":"user1",      "ip":"111.79.154.111"   },   {      "username":"user2",      "ip":"111.79.190.222"   }]
查看完整描述

3 回答

?
料青山看我应如是

TA贡献1772条经验 获得超8个赞

Go 程序在 main 函数结束时结束。

从语言规范

程序执行首先初始化主包,然后调用函数 main。当该函数调用返回时,程序退出。它不会等待其他(非主)goroutine 完成。

因此,您需要等待 goroutine 完成。对此的常见解决方案是使用sync.WaitGroup对象。

最简单的同步 goroutine 的代码:

package main


import "fmt"

import "sync"


var wg sync.WaitGroup // 1


func routine() {

    defer wg.Done() // 3

    fmt.Println("routine finished")

}


func main() {

    wg.Add(1) // 2

    go routine() // *

    wg.Wait() // 4

    fmt.Println("main finished")

}

并用于同步多个 goroutine


package main


import "fmt"

import "sync"


var wg sync.WaitGroup // 1


func routine(i int) {

    defer wg.Done() // 3

    fmt.Printf("routine %v finished\n", i)

}


func main() {

    for i := 0; i < 10; i++ {

        wg.Add(1) // 2

        go routine(i) // *

    }

    wg.Wait() // 4

    fmt.Println("main finished")

}

WaitGroup 按执行顺序使用。

  1. 全局变量的声明。使其全局化是使其对所有函数和方法可见的最简单方法。

  2. 增加计数器。这必须在 main goroutine 中完成,因为由于内存模型保证,不能保证新启动的 goroutine 会在 4 之前执行。

  3. 减少计数器。这必须在 goroutine 退出时完成。使用延迟调用,我们确保无论何时函数结束,无论它如何结束,它都会被调用。

  4. 等待计数器达到 0。这必须在 main goroutine 中完成以防止程序退出。

*在启动新 gouroutine 之前评估实际参数。因此需要在之前明确评估它们,wg.Add(1)以便可能出现恐慌的代码不会留下增加的计数器。


param := f(x)

wg.Add(1)

go g(param)

代替


wg.Add(1)

go g(f(x))


查看完整回答
反对 回复 2021-08-30
  • 3 回答
  • 0 关注
  • 558 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信