[解決済み] チャンネルを読まずに閉じたかどうかを確認する方法は?
質問
これは @Jimt によって書かれた、Go の workers & controller モードの良い例で、次のような答えになっています。 に対する回答です。 golangで他のgoroutineを一時停止&再開するエレガントな方法はありますか? "
package main
import (
"fmt"
"runtime"
"sync"
"time"
)
// Possible worker states.
const (
Stopped = 0
Paused = 1
Running = 2
)
// Maximum number of workers.
const WorkerCount = 1000
func main() {
// Launch workers.
var wg sync.WaitGroup
wg.Add(WorkerCount + 1)
workers := make([]chan int, WorkerCount)
for i := range workers {
workers[i] = make(chan int)
go func(i int) {
worker(i, workers[i])
wg.Done()
}(i)
}
// Launch controller routine.
go func() {
controller(workers)
wg.Done()
}()
// Wait for all goroutines to finish.
wg.Wait()
}
func worker(id int, ws <-chan int) {
state := Paused // Begin in the paused state.
for {
select {
case state = <-ws:
switch state {
case Stopped:
fmt.Printf("Worker %d: Stopped\n", id)
return
case Running:
fmt.Printf("Worker %d: Running\n", id)
case Paused:
fmt.Printf("Worker %d: Paused\n", id)
}
default:
// We use runtime.Gosched() to prevent a deadlock in this case.
// It will not be needed of work is performed here which yields
// to the scheduler.
runtime.Gosched()
if state == Paused {
break
}
// Do actual work here.
}
}
}
// controller handles the current state of all workers. They can be
// instructed to be either running, paused or stopped entirely.
func controller(workers []chan int) {
// Start workers
for i := range workers {
workers[i] <- Running
}
// Pause workers.
<-time.After(1e9)
for i := range workers {
workers[i] <- Paused
}
// Unpause workers.
<-time.After(1e9)
for i := range workers {
workers[i] <- Running
}
// Shutdown workers.
<-time.After(1e9)
for i := range workers {
close(workers[i])
}
}
しかし、このコードにも問題があります。でワーカーチャネルを削除したい場合
workers
というときに
worker()
が終了すると、デッドロックが発生します。
もし、あなたが
close(workers[i])
に書き込むと、閉じたチャネルに書き込むことができないため、 次回コントローラが書き込んだときにパニックが発生します。もし、mutex を使ってそれを保護した場合、それは
workers[i] <- Running
で止まってしまいます。
worker
はチャネルから何も読んでいないので、書き込みはブロックされ、mutex はデッドロックを引き起こします。回避策としてチャネルに大きなバッファを与えることもできますが、十分なものではありません。
というわけで、これを解決する最良の方法は
worker()
を使用して、もしコントローラが閉じたチャンネルを見つけたら、それを飛び越えて何もしないようにします。しかし、この状況でチャンネルがすでに閉じられているかどうかを確認する方法が見つかりません。コントローラでチャンネルを読み込もうとすると、コントローラがブロックされるかもしれません。というわけで、今のところ非常に困っています。
追記:パニックが発生したときのリカバリは私も試しましたが、パニックが発生したgoroutineを閉じてしまいます。この場合、コントローラになるので仕方ないですね。
それでも、次のバージョンのGoでこの機能を実装することは、Goチームにとって有益だと思います。
どのように解決するのですか?
パニックを起こしたチャネルを回復することで、書き込みを試みたチャネルに対して行うことができます。しかし、読み取りチャネルが閉じているかどうかは、そこから読み取ることなしに確認することはできません。
どちらか一方は
-
最終的にそこから "true" 値を読み取ります (
v <- c
) -
true"値と'not closed'インジケータを読み取ります (
v, ok <- c
) -
を読む
ゼロ値
と、「閉じた」インジケータ(
v, ok <- c
) ( 例 ) -
は、チャネルリードで永遠にブロックされます (
v <- c
)
最後の1つだけが技術的にチャンネルから読み込まれませんが、それはほとんど意味がありません。
関連
-
[解決済み] Goでマップにキーが含まれているかどうかを確認するには?
-
[解決済み] time.Sleepを使わずにすべてのゴルーチンが終了するのを待つには?
-
[解決済み] response.Bodyを閉じないとどうなりますか?
-
[解決済み] 単一値コンテキストにおける複数値
-
[解決済み] スライスを格納する interface{} 上の範囲
-
[解決済み] スライスを変数入力として渡すには?
-
[解決済み] 文字列を小文字に変換する方法を教えてください。
-
[解決済み] golangのスライスで要素を検索する方法
-
[解決済み] ゴルーチンからの戻り値をキャッチする
-
[解決済み] gc」と「gccgo」の主な相違点は何ですか?
最新
-
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 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] 単一値コンテキストにおける複数値
-
[解決済み] Goプログラムで静的リソースをバンドルする最良の方法は何ですか?
-
[解決済み] 現在時刻に時・分・秒を足す
-
[解決済み] GoLangで文字列を比較するにはどうしたらいいですか?
-
[解決済み] テキストファイルを文字列配列に読み込む(そして書き込む)
-
[解決済み] テンプレートでマップを繰り返し処理する
-
[解決済み] bytes.Buffer does not implement io.Writer" というエラーメッセージが表示される。
-
[解決済み] ローカルファイルからio.Readerを作成する
-
[解決済み] ...interface{}の意味 (ドット・ドット・ドット・インターフェース)
-
[解決済み] 空のスライスをチェックするにはどうしたらいいですか?