1

チャネルを監視し、チャネルを介して送信されたものをログに記録する関数を Go で作成しようとしています。

func monitorChannel(inChannel, outChannel reflect.Value, fid int64, cond *sync.Cond) {
    for {       
    cond.L.Lock()
    var toLog reflect.Value
    var ok bool
    for toLog, ok = inChannel.TryRecv() ; !toLog.IsValid(); { // while no value received
        if !ok {
            cond.L.Unlock()
            return
        }
        cond.Wait()
    }
    outChannel.Send(toLog)
    logMessage("a", "b", inChannel.Interface(), toLog.Interface(), fid)
    cond.L.Unlock()
}

}

この関数は、inChannel から受信し、送信されたメッセージをログに記録し、outChannel を介して送信することになっています。双方向チャネルをログに記録できるようにしたいので、ログに記録するチャネルごとにこの関数を 2 回呼び出し、inChannel と outChannel を入れ替えます。ロックは、2 つのゴルーチンが相互にメッセージを受け渡さないようにするためのものです。「fid」は、ログ ファイルの ID です。

しかし、次のテスト コードを実行すると、デッドロックが発生します。

errsIn := make(chan int64)
errsOut := make(chan int64)
cond := sync.NewCond(&sync.Mutex{})
go monitorChannel(reflect.ValueOf(errsIn), reflect.ValueOf(errsOut), fid, cond)
go monitorChannel(reflect.ValueOf(errsOut), reflect.ValueOf(errsIn), fid,  cond)
errsIn <- 1
if <-errsOut != 1 {
    t.Fatal("lost value through channel send")
}
errsOut <- 1
if <-errsIn != 1 {
    t.Fatal("lost value through channel send")
}

チャネルを閉じていないにもかかわらず、TryRecv が 2 番目の戻り値で false を返しているようです。どうしてこれなの?私はそれについて何をすべきですか?

Windows 8 64 ビットで go 1.0.3 を実行しています。

編集

後で、TryRecv の動作がやや混乱していることを発見し、reflect パッケージと 2 つの sync.Locker を使用して、関数の一般化されたバージョンを作成することができました。jnml のソリューションの方が洗練されていると思いますが、TryRecv で同様の問題を経験したことがある人は、関数の途中にあるコメントを見てください。

func passOnAndLog(in, out reflect.Value, l1, l2 sync.Locker) {
    for {
        l1.Lock()
        val, ok := in.TryRecv()
        for !val.IsValid() { // while nothing received
            l1.Unlock()
            time.Sleep(time.Nanosecond) // pausing current thread
            l1.Lock()
            val, ok = in.TryRecv()
        }
        // if val.IsValid() == true  and ok == false ,the channel is closed
        // if val.IsValid() == false and ok == false ,the channel is open but we received nothing
        // if val.IsValid() == true  and ok == true  ,we received an actual value from the open channel
        // if val.IsValid() == false and ok == true  ,we have no idea what happened  
        if !ok {
            return
        }
        l1.Unlock()
        l2.Lock() // don't want the other thread to receive while I am sending
        out.Send(val)
        LogValue(val) // logging

        l2.Unlock()
    }
}
4

1 に答える 1

2

リフレクションベースのソリューションは複雑すぎて、それが正しいか、または実行可能かどうかを怠惰に理解することはできません。(そうではないと思いますが、直感によるだけです。)

一般的ではありませんが、より簡単な方法でタスクにアプローチします。一部のプロデューサーが書き込みに使用し、一部のコンシューマーが読み取りに使用するチャネルを作成しましょう。

c := make(chan T, N)

たとえば、次のような小さなヘルパー関数を使用して、このチャネルを監視することができます。

func monitored(c chan T) chan T {
        m := make(chan T, M)
        go func() {
                for v := range c {
                        m <- v
                        logMessage(v)
                }
                close(m)
        }()
        return m
}

今では次のことで十分です。

mc := monitored(c)

  • c生産者に渡しますがmc、消費者に渡します。
  • ゴルーチンcが漏れないように、完了したら閉じます。

警告:上記のコードはまったくテストされていません。

于 2013-03-10T08:59:35.550 に答える