GO语言:致命错误:所有goroutines都睡着了 - 僵局 [英] GO language: fatal error: all goroutines are asleep - deadlock
问题描述
下面的代码可以很好地处理硬编码的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()
}
OUTPUTuser @ 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用法按执行顺序。
- 声明全局变量。使它成为全局是让所有函数和方法都可见的最简单的方法。
- 增加计数器。这必须在主要的例程中完成,因为不能保证新启动的goroutine会在4之前执行,这是由于内存模型保证。
- 减少柜台。这必须在goroutine出口处完成。使用延迟呼叫,我们确保无论如何,无论何时,无论何时,无论何时,无论何时结束,都会被调用
- 等待计数器达到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 usingsync.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.
- Declaration of global variable. Making it global is the easiest way to make it visible to all functions and methods.
- 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.
- 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.
- 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屋!