GO语言:致命错误:所有goroutines都睡着了 - 僵局 [英] GO language: fatal error: all goroutines are asleep - deadlock

查看:178
本文介绍了GO语言:致命错误:所有goroutines都睡着了 - 僵局的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码可以很好地处理硬编码的JSON数据,但是当我从文件读取JSON数据时不起作用。我得到致命错误:当使用 sync.WaitGroup 时,所有goroutines都睡着了 - 死锁错误。



具有硬编码JSON数据的工作示例:

 包主要

导入(
字节
fmt
os / exec
时间


func connect(主机字符串){
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\\\
,host,out.String())
time.Sleep(time.Second * 2)
fmt.Printf(%s:DONE \\\
,主机)
}

func监听器(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 st戒指)
去监听(c)

for i:= 0;我< LEN(主机); i ++ {
c< - hosts [i]
}
var input string
fmt.Scanln(& input)
}

$ b

输出:

  user @ user-VirtualBox:〜/ go $ go run channel.go 
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 86天,17:27,1个用户,平均负载:9
user1@111.79.154.111:DONE
user2 @ 111.79.190.222:DONE

不工作 - 读取JSON数据文件的示例:

 包主

导入(
字节
fmt
os / exec
time
encoding / json
os
sync


func connect(主机字符串){
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\\\
,host,out.String())
time.Sleep(time.Second * 2)
fmt.Printf(%s:DONE \ n,主机)
}

func侦听器(c chan字符串){
for {
host:= < -c
连接(主机)
}
}

类型内容结构{
用户名字符串`json:username`
Ip string`json:ip`
}

func main(){
var wg sync.WaitGroup

var source []内容
var hosts []字符串
data:= json.NewDecoder(os.Stdin)
data.Decode(& source)

for _,value: = range source {
hosts = append(hosts,value.Username +@+ value.Ip)
}

var c chan string = make(chan string)
去监听器(c)

for i:= 0;我< LEN(主机); i ++ {
wg.Add(1)
c< - hosts [i]
defer wg.Done()
}

var input string
fmt.Scanln(& input)

wg.Wait()
}



OUTPUT

  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个86天,17:27,1个用户,平均负载:9
user1@111.79.154.111:DONE
user2@111.79.190.222:DONE
致命错误:所有goroutines都睡着了 - 僵局!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc210000068)
/usr/lib/go/src/pkg/runtime/sema.goc:199 + 0x30 $ b (* WaitGroup).Wait(0xc210047020)
/usr/lib/go/src/pkg/sync/waitgroup.go:127 + 0x14b
main.main()
/home/user/go/deploy.go:64 + 0x45a

goroutine 3 [chan receive]:
main.listener(0xc210038060)
/ home / user / go / deploy.go:28 + 0x30
由main.main创建
/home/user/go/deploy.go:53 + 0x30b
退出状态2
user @ user-VirtualBox :〜/ go

HOSTS.TXT

  [
{
username:user1,
ip:111.79.154.111
},
{
username:user2,
ip:111.79.190.222
}
]


解决方案

Go程序在主功能结束时结束。

语言规范


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


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



同步goroutine的最简单的代码:

 包主

importfmt
importsync

var wg sync.WaitGroup // 1

func routine(){
defer wg.Done ()// 3
fmt.Println(routine finished)
}

func main(){
wg.Add(1)// 2
去例程()// *
wg.Wait()// 4
fmt.Println(main finished)
}

同步多个goroutines

  package main 

导入fmt
导入sync

var wg sync.WaitGroup // 1

函数例程(i int) {
defer wg.Done()// 3
fmt.Printf(routine%v finished \\\
,i)
}

func main( ){
for i:= 0;我< 10; i ++ {
wg.Add(1)// 2
去例程(i)// *
}
wg.Wait()// 4
fmt。 PrintIn(主完成)
}

WaitGroup用法按执行顺序。


  1. 声明全局变量。使它成为全局是让所有函数和方法都可见的最简单的方法。

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

  3. 减少柜台。这必须在goroutine出口处完成。使用延迟呼叫,我们确保无论如何,无论何时,无论何时,无论何时,无论何时结束,都会被调用
  4. 等待计数器达到0.这必须在主程序中完成,以防止程序退出。

*实际参数在开始新gouroutine之前进行评估。因此需要在 wg.Add(1)之前显式评估它们,因此可能的恐慌代码不会留下增加的计数器。



使用

  param:= f(x)
wg.Add(1)
go g(param)

而不是

  wg.Add(1)
go g(f(x))


Code below works fine with hard coded JSON data however doesn't work when I read JSON data from a file. I'm getting fatal error: all goroutines are asleep - deadlock error when using sync.WaitGroup.

WORKING EXAMPLE WITH HARD-CODED JSON DATA:

package main

import (
    "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)
}

OUTPUT:

user@user-VirtualBox:~/go$ go run channel.go
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: DONE
user2@111.79.190.222: DONE

NOT WORKING - EXAMPLE WITH READING JSON DATA FILE:

package main

import (
    "bytes"
    "fmt"
    "os/exec"
    "time"
    "encoding/json"
    "os"
    "sync"
)

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)
    }
}

type Content struct {
    Username string `json:"username"`
    Ip       string `json:"ip"`
}

func main() {
    var wg sync.WaitGroup

    var source []Content
    var hosts []string
    data := json.NewDecoder(os.Stdin)
    data.Decode(&source)

    for _, value := range source {
        hosts = append(hosts, value.Username + "@" + value.Ip)
    }

    var c chan string = make(chan string)
    go listener(c)

    for i := 0; i < len(hosts); i++ {
        wg.Add(1)
        c <- hosts[i]
        defer wg.Done()
    }

    var input string
    fmt.Scanln(&input)

    wg.Wait()
}

OUTPUT

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 : DONE
user2@111.79.190.222: DONE
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc210000068)
    /usr/lib/go/src/pkg/runtime/sema.goc:199 +0x30
sync.(*WaitGroup).Wait(0xc210047020)
    /usr/lib/go/src/pkg/sync/waitgroup.go:127 +0x14b
main.main()
    /home/user/go/deploy.go:64 +0x45a

goroutine 3 [chan receive]:
main.listener(0xc210038060)
    /home/user/go/deploy.go:28 +0x30
created by main.main
    /home/user/go/deploy.go:53 +0x30b
exit status 2
user@user-VirtualBox:~/go$

HOSTS.TXT

[
   {
      "username":"user1",
      "ip":"111.79.154.111"
   },
   {
      "username":"user2",
      "ip":"111.79.190.222"
   }
]

解决方案

Go program ends when the main function ends.

From the language specification

Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.

Therefore, you need to wait for your goroutines to finish. The common solution for this is to use sync.WaitGroup object.

The simplest possible code to synchronize 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")
}

And for synchronizing multiple goroutines

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 usage in order of execution.

  1. Declaration of global variable. Making it global is the easiest way to make it visible to all functions and methods.
  2. Increasing the counter. This must be done in main goroutine because there is no guarantee that newly started goroutine will execute before 4 due to memory model guarantees.
  3. Decreasing the counter. This must be done at the exit of goroutine. Using deferred call, we make sure that it will be called whenever function ends no matter but no matter how it ends.
  4. Waiting for the counter to reach 0. This must be done in main goroutine to prevent program exit.

* The actual parameters are evaluated before starting new gouroutine. Thus it is needed to evaluate them explicitly before wg.Add(1) so the possibly panicking code would not leave increased counter.

Use

param := f(x)
wg.Add(1)
go g(param)

instead of

wg.Add(1)
go g(f(x))

这篇关于GO语言:致命错误:所有goroutines都睡着了 - 僵局的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆