下面的代码可以很好地处理硬编码的JSON数据,但是当我从文件中读取JSON数据时就不起作用了。使用sync.WaitGroup
时出现fatal error: all goroutines are asleep - deadlock
错误。
使用硬编码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$
HOSTS.TXT
[
{
"username":"user1",
"ip":"111.79.154.111"
},
{
"username":"user2",
"ip":"111.79.190.222"
}
]
4条答案
按热度按时间41ik7eoe1#
当main函数结束时,Go程序结束。
从语言规范
程序执行首先初始化main包,然后调用函数main。当该函数调用返回时,程序退出。它不会等待其他(非主)goroutine完成。
因此,你需要等待你的goroutines完成。常见的解决方案是使用sync.WaitGroup对象。
同步goroutine的最简单代码:
和同步多个goroutine
按执行顺序排列的WaitGroup用法。
1.全局变量的声明。使其全局化是使其对所有函数和方法可见的最简单方法。
1.增加计数器。这必须在main goroutine中完成,因为由于内存模型的保证,不能保证新启动的goroutine会在4之前执行。
1.减少计数器。这必须在goroutine的出口处完成。使用延迟调用,我们确保它将被调用时,函数结束,无论但无论如何结束。
1.等待计数器达到0。这必须在main goroutine中完成,以防止程序退出。
wg.Add(1)
之前显式地评估它们,以便可能的恐慌代码不会留下增加的计数器。使用
而不是
7vux5j2d2#
感谢您的非常好和详细的解释Grzegorz ur。我想指出的一点是,通常需要线程化的func不会在
main()
中,所以我们会有这样的东西:我想指出的是,
wg
的全局声明对于所有线程在main()
之前完成非常重要6qfn3psc3#
vkc1a9a24#
试试这个代码片段