8

すべて同じメッセージを受信するチャネルのスライスがあります。

func broadcast(c <-chan string, chans []chan<- string) {
    for msg := range c {
        for _, ch := range chans {
            ch <- msg
        }
    }
}

ただし、各チャネルはchans異なる速度で読み取られる可能性があるため、コンシューマが遅いときに他のチャネルをブロックしたくありません。私はゴルーチンでこれを解決しました:

func broadcast(c <-chan string, chans []chan<- string) {
    for msg := range c {
        for _, ch := range chans {
            go func() { ch <- msg }()
        }
    }
}

ただし、各チャネルに渡されるメッセージの順序は重要です。仕様を調べて、ブロックされたときにチャネルが順序を保持するかどうかを確認しましたが、見つかったのはこれだけでした:

容量が 0 より大きい場合、チャネルは非同期です。バッファーがいっぱいでない (送信) または空でない (受信) 場合、通信操作はブロックされることなく成功し、要素は送信された順序で受信されます。

私にとって、書き込みがブロックされている場合、それは「送信」されていませんが、送信されるのを待っています。その前提で、上記は、書き込み時に複数のゴルーチンがブロックされている場合の送信順序について何も述べていません。

チャネルのブロックが解除された後の送信順序について保証はありますか?

4

4 に答える 4

4

チャネルがいっぱいの場合はメッセージをドロップできます (その後、フラグを設定してクライアントを一時停止し、メッセージをドロップしているというメッセージなどを送信します)。

(テストされていない)の行に沿ったもの:

type Client struct {
    Name string
    ch   chan<-string
}

func broadcast(c <-chan string, chans []*Client) {
    for msg := range c {
        for _, ch := range chans {
            select {
            case ch.ch <- msg:
            // all okay
            default:
                log.Printf("Channel was full sending '%s' to client %s", msg, ch.Name)
            }
        }
    }
}
于 2013-04-07T04:35:03.737 に答える
1

このコードでは、保証はありません。

上記のサンプル コードの主な問題は、チャネルの動作ではなく、作成された多数のゴルーチンにあります。すべてのゴルーチンは、さらに同期することなく、同じ複雑なループ内で「起動」されるため、メッセージの送信を開始する前であっても、どのゴルーチンが最初に実行されるかはわかりません。

しかし、これは一般的に正当な疑問を引き起こします: 何らかの方法でいくつかのブロッキング送信命令の順序を保証する場合、それらを同じ順序で受け取ることが保証されますか?

送信の「前に発生」プロパティを作成するのは困難です。次の理由により、それは不可能だと思います。

  1. 送信命令の前に何かが発生する可能性があります。たとえば、他のゴルーチンが独自の送信を実行するかどうかなどです。
  2. 送信でブロックされているゴルーチンは、他の種類の同期を同時に管理することはできません

たとえば、1 から 10 までの番号が付けられた 10 個のゴルーチンがある場合、それらが独自の番号をチャネルに同時に正しい順序で送信する方法がありません。私にできることは、1 つのゴルーチンでソートを行うなど、さまざまな種類の順次トリックを使用することだけです。

于 2013-04-07T11:57:05.120 に答える
0

これは、すでに投稿された回答への追加です。

事実上誰もが述べたように、問題はゴルーチンの実行順序にある​​ため、実行したいゴルーチンの番号を渡すことで、チャネルを使用してゴルーチンの実行を簡単に調整できます。

func coordinated(coord chan int, num, max int, work func()) {
    for {
        n := <-coord

        if n == num {
            work()
            coord <- (n+1) % max
        } else {
            coord <- n
        }
    }
}

coord := make(chan int)

go coordinated(coord, 0, 3, func() { println("0"); time.Sleep(1 * time.Second) })
go coordinated(coord, 1, 3, func() { println("1"); time.Sleep(1 * time.Second) })
go coordinated(coord, 2, 3, func() { println("2"); time.Sleep(1 * time.Second) })

coord <- 0

または、順序付けられた方法でワーカーを実行する中央ゴルーチンを使用します。

func executor(funs chan func()) {
    for {
        worker := <-funs
        worker()
        funs <- worker
    }
}

funs := make(chan func(), 3)

funs <- func() { println("0"); time.Sleep(1 * time.Second) }
funs <- func() { println("1"); time.Sleep(1 * time.Second) }
funs <- func() { println("2"); time.Sleep(1 * time.Second) }

go executor(funs)

もちろん、これらのメソッドは、同期によるすべての並列処理を削除します。ただし、プログラムの同時実行の側面は残ります。

于 2013-04-07T16:35:20.260 に答える