2016-05-13 13 views
4

デッドロックを発生させる私のプログラムはどうですか?これを回避するにはどうしたらいいですか?このような状況を処理するための推奨パターンは何ですか?このgolangプログラムでデッドロックを回避するにはどうすればよいですか?

タイムアウト後に問題が発生するのは、自分のチャンネルにリーダーがないことをどのように検出するのですか?

var wg sync.WaitGroup 

func main() { 
    wg.Add(1) 
    c := make(chan int) 
    go readFromChannel(c, time.After(time.Duration(2)*time.Second)) 
    time.Sleep(time.Duration(5) * time.Second) 
    c <- 10 
    wg.Wait() 
} 

func readFromChannel(c chan int, ti <-chan time.Time) { 
    select { 
    case x := <-c: 
     fmt.Println("Read", x) 
    case <-ti: 
     fmt.Println("TIMED OUT") 
    } 
    wg.Done() 
} 

答えて

1

チャンネルがバッファされていません。受信機は値を 受信するまでチャネルは、送信側ブロックバッファリングされていない場合

docsによります。チャネルは、バッファ、送信者ブロック値がバッファリングされることにチャンネルを変更することにより、バッファ

にコピーされているだけまで を持っている場合、我々はデッドロックを回避することができます。

c := make(chan int, 10) // holds 10 ints 

私もhttps://golang.org/doc/effective_go.html#channelsを読んで、それはチャンネルにそこ関連して、いくつかの良いものを持ってお勧めします。

6

だから、実際に何が起きているのかを見てみましょう。 2つののゴルーチンがあります(2つ以上ありますが、明示的なものに焦点を当てます)。mainreadFromChannelです。

readFromChannelが何をするのか見てみましょう:

if channel `c` is not empty before `ti` has expired, print its contents and return, after signalling its completion to wait group. 
if `ti` has expired before `c` is not empty, print "TIMED OUT" and return, after signalling its completion to wait group. 

は現在メイン:

adds to waitgroup 
make a channel `c` 
start a goroutine `readFromChannel` 
sleep for 5 seconds 
send 10 to channel `c` 
call wait for waitgroup 

は今、(あなたのコードは/実行されない可能性があり、同時に、あなたのコードの実行の流れを通過することができます毎回この順番でこれを覚えておいてください)

1) wg.Add(1) 
2) c := make(chan int) 
3) go readFromChannel(c, time.After(time.Duration(2)*time.Second)) 
#timer ti starts# 
4) time.Sleep(time.Duration(5) * time.Second) 
#MAIN Goroutine begins sleep 
#timer ti expires# 
5) case <-ti: 
6) fmt.Println("TIMED OUT") 
7) wg.Done() 
# readFromChannel Goroutine returns # 
#MAIN Goroutine exits sleep# 
8) c<-10 
9) ......#DEADLOCK# 

なぜあなたはデッドロックを持っているのですか?外出先では、バッファされていないチャンネルは、送信中か受信中かにかかわらず、チャンネルの反対側で何かが発生するまで、をブロックします。だからc <- 10は、何かがcのもう一方の端から読み込まれるまでブロックしますが、そのために持っていたゴルーチンは2秒前に画像から脱落しました。したがって、cは永遠にブロックし、mainは最後のゴルーチンですので、デッドロックが発生します。

どうすればいいですか?チャンネルを使用する場合は、sendごとに常にチャンネルの反対側にreceiveがあることを確認してください。バッファされたチャネルを使用することもできますが、上記のコードでは、 "正しい"解決策ではありません。

はここでデッドロックのための私の修正です:

func main() { 
    wg.Add(1) 
    c := make(chan int) 
    go readFromChannel(c, time.After(time.Duration(2)*time.Second)) 
    time.Sleep(time.Duration(5) * time.Second) 
    c <- 10 
    wg.Wait() 
} 

func readFromChannel(c chan int, ti <-chan time.Time) { 
     // the forloop will run forever 
    loop: // ** 
    for { 
     select { 
      case x := <-c: 
        fmt.Println("Read", x) 
        break loop // breaks out of the for loop and the select ** 
      case <-ti: 
        fmt.Println("TIMED OUT") 
      } 
    } 
    wg.Done() 
} 

** see this answer for details

+0

これは、私はいくつかの時間に読んだ最も有用な答えの一つです。私がフォローしていることを確かめるために、「修正」は、タイムアウト後も受信者のチャンネルを維持するので機能します。だから 'wg.Done()'( 'main' goルーチンを終了する)は、何かが' c'から読み込まれた場合にのみ発生します。 –

+1

そうだけど、ちょっとクリアーするには、** goroutine **の実行を保つ。 – AJPennster

0

あなたの問題はあなたがselectステートメントを使用しているが、あなたはゴルーチン内で使用していないということです。

go func() { 
    for { 
     select { 
     case x := <-c: 
      fmt.Println("Read", x) 
     case <-ti: 
      fmt.Println("TIMED OUT") 
     } 
    } 
}() 

異なる同時に実行ゴルーチンのうち値が密接スイッチ制御ステートメントに似ており、時には通信がスイッチと呼ばれる選択キーワードを用いて達成することができる取得します。

デフォルトの大文字小文字のselect文でsendオペレーションを使用すると、sendが非ブロックになることが保証されます。ケースがない場合、selectは実行を永遠にブロックします。

https://play.golang.org/p/Ai1ggveb4s

0

これは古い質問ですが、私は自分自身の学習チャネルへの深いダイビングだとここでこれを見つけました。

私はあなたがそれを送信した後にチャンネルを閉じる必要があると思いますか?

コード:

func main() { 
    wg.Add(1) 
    c := make(chan int) 
    go readFromChannel(c, time.After(time.Duration(2)*time.Second)) 
    time.Sleep(time.Duration(5) * time.Second) 
    c <- 10 
    close(c) // <- CLOSE IT HERE 
    wg.Wait() 
} 
関連する問題