2016-05-24 8 views
1

複数のゴルーチンがnotifyAll funcを実行する場合、ロックなしでレンジマップを設定することは安全ですか?実際にある範囲では、時々マップからエントリを削除する必要があります。範囲の前にRLockマップする必要がありますか?

var mu sync.RWMutex 

func (self *Server) notifyAll(event *Event) 
    ch := make(chan int, 64) 
    num := 0 
    for k, v := range self.connections { 
    num++ 
    ch <- num 

    go func(int k, conn *Conn) { 
     err := conn.sendMessage(event) 
     <-ch 
     if err != nil { 
     self.removeConn(k) 
     } 
    }(k, v) 
    } 
} 

func (self *Server) removeConn(int k) { 
    mu.Lock() 
    defer mu.Unlock() 
    delete(self.connections, k) 
} 

// Somewhere in another goroutine 
func (self *Server) addConn(conn *Conn, int k) { 
    mu.Lock() 
    defer mu.Unlock() 
    self.connections[k] = conn 
} 

範囲の前にRLockマップを作成する必要がありますか?

func (self *Server) notifyAll(event *Event) 
    mu.RLock() 
    defer mu.RUnlock() 
    // Skipped previous body... 
} 
+0

「自己」の問題は何ですか、これはOCDの一種ですか? – Alex

+0

実際には、Goクリエイター自身、コミュニティの大部分と一緒に見下されています。 https://github.com/golang/go/wiki/CodeReviewComments#receiver-namesもhttp://programmers.stackexchange.com/questions/286406/use-of-this-in-golangを確認してください – OneOfOne

答えて

0

あなたは同時がremoveConnでの書き込みとnotifyAllに読み込みを防止するためにマップをロックする必要があります。

+0

私のコードは正しいです2番目の例の部分はありますか? – Alex

0

短い答え:マップは並行安全ではありません(まだスレッドセーフと言えます)。

異なるジャンプルーチンからマップにアクセスする必要がある場合は、何らかの形式のアクセスオーケストレーションを使用する必要があります。そうしないと、「制御されていないマップアクセスによってプログラムがクラッシュする可能性があります」(this参照)。

編集:

これは別の実装である - ミューテックスをすべて一緒に無視し、より多くのGoishアプローチを使用しています(ハウスキーピングの懸念を考慮せずにタイムアウトを終了し、ログなど)(これは、単にこれを実証するためでありますアクセスオーケストレーションの懸念をクリアするために私たちを助けアプローチ - )あなたのケースのために正しいかではないかもしれません。

type Server struct { 
    connections map[*Conn]struct{} 

    _removeConn, _addConn chan *Conn 
    _notifyAll   chan *Event 
} 

func NewServer() *Server { 
    s := new(Server) 
    s.connections = make(map[*Conn]struct{}) 
    s._addConn = make(chan *Conn) 
    s._removeConn = make(chan *Conn, 1) 
    s._notifyAll = make(chan *Event) 
    go s.agent() 
    return s 
} 

func (s *Server) agent() { 
    for { 
     select { 
     case c := <-s._addConn: 
      s.connections[c] = struct{}{} 
     case c := <-s._removeConn: 
      delete(s.connections, c) 
     case e := <-s._notifyAll: 
      for c := range s.connections { 
       closure := c 
       go func() { 
        err := closure.sendMessage(e) 
        if err != nil { 
         s._removeConn <- closure 
        } 
       }() 
      } 
     } 
    } 
} 

func (s *Server) removeConn(c *Conn) { 
    s._removeConn <- c 
} 

func (s *Server) addConn(c *Conn) { 
    s._addConn <- c 
} 

編集:

私は修正されました。 Damian Gryskiによると、マップは同時読み取りで安全です。マップ順序が各反復で変化する理由は、「マップ反復順序のために選択されたランダムシード」​​であり、これはゴルーチンの反復に対して局所的である(別のtweet)。この事実は、最初の編集と提案された解決策には影響しません。

+0

2番目のコード例がありますが、この場合正しいですか? – Alex

+0

いいえ。 'notifyAll'が終了し、' defer mu.RUnlock() 'が実行された後に開始される可能性のある別のゴーイングで項目を削除しているためです。言い換えれば、 'notifyAll'の中にあなたのマップをロックすることは、あなたが内部のゴールーチン内で行うこととは何の関係もありません。 –

+0

私はmu.Lock()で項目を削除しています。単純な削除ではありません。どのようにしなければならないか教えていただけますか? – Alex

関連する問題