2016-12-16 3 views
1

私はこのコードを書いたので、プロジェクトの動的バッファで非ブロックチャネルが必要です。ここで Goで選択ステートメント以外の条件を移動したときにこのデッドロックが発生する理由

は型宣言です:

//receiver is the receiver of the non blocking channel 
type receiver struct { 
    Chan <-chan string 
    list *[]string 
    mutex sync.RWMutex 
} 

//Clear sets the buffer to 0 elements 
func (r *receiver) Clear() { 
    r.mutex.Lock() 
    *r.list = (*r.list)[:0] 
    r.mutex.Unlock() 
    //Discards residual content 
    if len(r.Chan) == 1 { 
     <-r.Chan 
    } 
} 

コンストラクタ:

//NewNonBlockingChannel returns the receiver & sender of a non blocking channel 
func NewNonBlockingChannel() (*receiver, chan<- string) { 
    //Creates the send and receiver channels and the buffer 
    send := make(chan string) 
    recv := make(chan string, 1) 
    list := make([]string, 0, 20) 

    r := &receiver{Chan: recv, list: &list} 

    go func() { 

     for { 
      //When the receiver is empty sends the next element from the buffer 
      if len(recv) == 0 && len(list) > 0 { 
       r.mutex.Lock() 
       recv <- list[len(list)-1] 
       list = list[:len(list)-1] 
       r.mutex.Unlock() 
      } 

      select { 
      //Adds the incoming elements to the buffer 
      case s := <-send: 
       r.mutex.Lock() 
       list = append(list, s) 
       r.mutex.Unlock() 
       //default: 

      } 
     } 
    }() 

    return r, send 
} 

そしてメインでおもちゃのテスト:

func main() { 
    recv, sender := NewNonBlockingChannel() 

    //send data to the channel 
    go func() { 
     for i := 0; i < 5; i++ { 
      sender <- "Hi" 
     } 
     time.Sleep(time.Second) 
     for i := 0; i < 5; i++ { 
      sender <- "Bye" 
     } 
    }() 
    time.Sleep(time.Millisecond * 70) //waits to receive every "Hi" 
    recv.Clear() 
    for data := range recv.Chan { 
     println(data) 
    } 

} 

私はそれをテストし、デッドロックがで起こりました送信者から受け取ったときのselectステートメント "ケース:= < -send: "が、私はselect文のすべてが完璧に動作に次のバッファリングされた文字列を送信し、条件付きブロックを移動する場合:

go func() { 

    for { 
     select { 
     //Adds the incoming elements to the buffer 
     case s := <-send: 
      r.mutex.Lock() 
      list = append(list, s) 
      r.mutex.Unlock() 
     default: 
      //When the receiver is empty sends the next element from the buffer 
      if len(recv) == 0 && len(list) > 0 { 
       r.mutex.Lock() 
       recv <- list[len(list)-1] 
       list = list[:len(list)-1] 
       r.mutex.Unlock() 
      } 
     } 
    } 
}() 

を私は理由を知りたいのですが。

+0

慣用のGoをしたい場合は、おそらくチャネルとロックをミックスしないでください。 *共有メモリで通信しないでください。代わりに、通信でメモリを共有してください。* https://blog.golang.org/share-memory-by-communicating –

+0

しかし、私はスライスにアクセスする際にスレッドの安全のためにロックが必要です。サービスの性質上、非ブロッキングチャネルが必要なので、スライスをデータのバッファとして使用することにします。 –

答えて

0

例えば、このシナリオを想像:

「BYE」、これが3つあるためである( listに残されている

for data := range recv.Chan { 
     println(data) 
} 

3メイン、彼ら二人がされている印刷によって送信されてきた

5時間はforループを入力し、条件len(recv) == 0 && len(list) > 0は偽なので、それだけでcase s := <-sendを満たし、かつlist = append(list, s) 3回行う)

とによって開始されたゴルーチンは、s := <-sendというステートメントで待機しています。受信するためのより多くのスタッフがいない何も、それはあなたがコードselect

if len(recv) == 0 && len(list) > 0 { 
    r.mutex.Lock() 
    recv <- list[len(list)-1] 
    list = list[:len(list)-1] 
    r.mutex.Unlock() 
} 

を移動すると、物事が異なっている永遠

をお待ちしておりますので:待機gorountineが目を覚ますだろう

、および条件if len(recv) == 0 && len(list) > 0はちょうど良い時期です。物事はうまくいく。

関連する問題