[解決済み] GO言語:致命的なエラー:すべてのゴルーチンがスリープしている - デッドロック
2022-02-01 06:01:37
質問
以下のコードは、ハードコードされたJSONデータでは問題なく動作しますが、ファイルからJSONデータを読み込むと動作しません。以下のようになります。
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{"[email protected]", "[email protected]"}
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
[email protected]: " 09:46:40 up 86 days, 18:16, 0 users, load average: 5"
[email protected]: " 09:46:40 up 86 days, 17:27, 1 user, load average: 9"
[email protected]: DONE
[email protected]: 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()
}
OUTPUT
user@user-VirtualBox:~/go$ go run deploy.go < hosts.txt
[email protected]: " 09:46:40 up 86 days, 18:16, 0 users, load average: 5"
[email protected]: " 09:46:40 up 86 days, 17:27, 1 user, load average: 9"
[email protected] : DONE
[email protected]: 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のプログラムはmain関数が終了すると終了します。
より 言語仕様
プログラムの実行は、main パッケージの初期化から始まり、main 関数を呼び出す。この関数の呼び出しが戻ると、プログラムは終了します。他の(main 以外の)ゴルーチンの完了を待つことはありません。
したがって、ゴルーチンが終了するのを待つ必要があります。これに対する一般的な解決策は sync.WaitGroup オブジェクトを作成します。
ゴルーチンを同期させるための最も単純なコードです。
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")
}
また、複数のゴルーチンを同期させるために
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の使用法を実行順に並べたものです。
- グローバル変数の宣言。グローバルにすることは、すべての関数やメソッドから見えるようにする最も簡単な方法です。
- カウンターを増加させる。メモリモデルの関係で、新しく起動したゴルーチンが4より前に実行される保証はないので、これはメインゴルーチンの中で行う必要がある ギャランティー .
- カウンターをデクリメントする。これはgoroutineの終了時に行う必要があります。遅延呼び出しを使って、確実に 関数が終了するたびに呼び出される が、どのように終了しようとも、関係ありません。
- カウンタが0になるのを待つ。プログラムの終了を防ぐため、メインゴルーチンの中で行わなければならない。
* 実際のパラメータは
新しいgouroutineを開始する前に評価されます。
. そのため,以下のように明示的に評価する必要があります.
wg.Add(1)
そのため、パニックを起こす可能性のあるコードがカウンターを増やさないようにするためです。
使用する
param := f(x)
wg.Add(1)
go g(param)
ではなく
wg.Add(1)
go g(f(x))
関連
-
go build error, error can't load package: package xxx is not in GOROOT (D:\basic_tool╱GoGoSrc╱)
-
[解決済み] このキャスティングはgolangで行われているのでしょうか?
-
[解決済み] Golang 構造体へのキャストインターフェイス
-
[解決済み] 全モジュールのアップデートを行う
-
[解決済み] golangでnilはどういう意味ですか?
-
[解決済み】Goのタグはどのような用途に使われますか?
-
[解決済み】init()関数はいつ実行されるのですか?
-
[解決済み】マップからキーのスライスを取得する
-
[解決済み】Goはサードパーティ・パッケージをどのようにアップデートしますか?
-
[解決済み】チャンネルを開いたままにしておいてもいいのでしょうか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】golangでデータ構造をディープコピーする
-
[解決済み] Goで配列を反転させるにはどうしたらいいですか?
-
[解決済み] Goプロセスをフォークする方法を教えてください。
-
[解決済み] panic: ランタイムエラー:Goのインデックスが範囲外
-
[解決済み] キューの実装はありますか?
-
[解決済み] Goにforeachループはあるのか?
-
[解決済み] ゼロ終端のバイト配列を文字列に変換するにはどうすればよいですか?
-
[解決済み] Goでオブジェクトの型を見つけるには?
-
[解決済み】マップからキーのスライスを取得する
-
[解決済み】Goの構造体のスタックとヒープ割り当て、およびガベージコレクションとの関連性