下面的代码与硬编码的JSON数据一起正常工作,但是当我从文件中读取JSON数据时不起作用。我得到fatal error: all goroutines are asleep - deadlock使用时的错误sync.WaitGroup。
fatal error: all goroutines are asleep - deadlock
sync.WaitGroup
硬编码JSON数据的工作示例:
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) }
输出:
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
不起作用-读取JSON数据文件的示例:
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() }
输出值
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$
主机
[ { "username":"user1", "ip":"111.79.154.111" }, { "username":"user2", "ip":"111.79.190.222" } ]
当主功能结束时,Go程序结束。
从语言规范
程序执行首先初始化主程序包,然后调用函数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按执行顺序使用。
*实际参数在开始新的鱼肉素之前要进行评估。因此,需要在对它们进行显式评估之前,wg.Add(1)使可能出现恐慌的代码不会留下增加的计数器。
wg.Add(1)
用
param := f(x) wg.Add(1) go g(param)
代替
wg.Add(1) go g(f(x))