43

両方のチャンネルが排水されたときにブロックされ、2 つのチャンネルで定期的にリッスンしたいと考えています。ただし、両方のチャネルにデータが含まれている場合は、一方が処理される前に一方が排出されるようにします。

out以下の実際の例では、処理する前にすべてを排出したいと考えていexitます。select優先順位のないステートメントを使用しています。出口の前に 10 個のアウト値をすべて処理するようにするには、どうすれば問題を回避できますか?

package main

import "fmt"

func sender(out chan int, exit chan bool){
    for i := 1; i <= 10; i++ {
        out <- i
    } 
    exit <- true
}

func main(){
    out := make(chan int, 10)
    exit := make(chan bool)

    go sender(out, exit)

    L:
    for {
        select {
            case i := <-out:
                fmt.Printf("Value: %d\n", i)
            case <-exit:
                fmt.Println("Exiting")
                break L
        }
    }
    fmt.Println("Did we get all 10? Most likely not")
}
4

9 に答える 9

42

言語はこれをネイティブにサポートしており、回避策は必要ありません。非常に簡単です。quit チャネルはプロデューサーにのみ表示される必要があります。終了すると、プロデューサーはチャネルを閉じます。チャネルが空で閉じられている場合にのみ、コンシューマーは終了します。これは、チャネルをまたぐことによって可能になります。

以下に例を示します。

package main

import (
    "fmt"
    "math/rand"
    "time"
)

var (
    produced  = 0
    processed = 0
)

func produceEndlessly(out chan int, quit chan bool) {
    defer close(out)
    for {
        select {
        case <-quit:
            fmt.Println("RECV QUIT")
            return
        default:
            out <- rand.Int()
            time.Sleep(time.Duration(rand.Int63n(5e6)))
            produced++
        }
    }
}

func quitRandomly(quit chan bool) {
    d := time.Duration(rand.Int63n(5e9))
    fmt.Println("SLEEP", d)
    time.Sleep(d)
    fmt.Println("SEND QUIT")
    quit <- true
}

func main() {
    vals, quit := make(chan int, 10), make(chan bool)
    go produceEndlessly(vals, quit)
    go quitRandomly(quit)
    for x := range vals {
        fmt.Println(x)
        processed++
        time.Sleep(time.Duration(rand.Int63n(5e8)))
    }
    fmt.Println("Produced:", produced)
    fmt.Println("Processed:", processed)
}
于 2012-06-21T04:59:31.797 に答える
32
package main

import "fmt"

func sender(out chan int, exit chan bool) {
    for i := 1; i <= 10; i++ {
        out <- i
    }
    exit <- true
}

func main() {
    out := make(chan int, 10)
    exit := make(chan bool)

    go sender(out, exit)

    for {
        select {
        case i := <-out:
            fmt.Printf("Value: %d\n", i)
            continue
        default:
        }
        select {
        case i := <-out:
            fmt.Printf("Value: %d\n", i)
            continue
        case <-exit:
            fmt.Println("Exiting")
        }
        break
    }
    fmt.Println("Did we get all 10? I think so!")
}

最初の選択のデフォルトのケースでは、ブロックされません。select は、出口チャネルを見ずに out チャネルを排出しますが、それ以外の場合は待機しません。出力チャネルが空の場合、すぐに 2 番目の選択にドロップされます。2 番目の選択はブロッキングです。いずれかのチャネルでデータを待ちます。出口が来ると、それを処理し、ループを終了させます。データが来ると、ループの先頭に戻り、ドレイン モードに戻ります。

于 2012-06-20T14:17:01.530 に答える
6

別のアプローチ:

package main

import "fmt"

func sender(c chan int) chan int {
        go func() {
                for i := 1; i <= 15; i++ {
                        c <- i
                }
                close(c)
        }()
        return c
}

func main() {
        for i := range sender(make(chan int, 10)) {
                fmt.Printf("Value: %d\n", i)
        }
        fmt.Println("Did we get all 15? Surely yes")
}

$ go run main.go
Value: 1
Value: 2
Value: 3
Value: 4
Value: 5
Value: 6
Value: 7
Value: 8
Value: 9
Value: 10
Value: 11
Value: 12
Value: 13
Value: 14
Value: 15
Did we get all 15? Surely yes
$ 
于 2012-06-20T10:45:57.487 に答える
1

かなり単純な回避策を 1 つ作成しました。それは私が望むことをしますが、他の誰かがより良い解決策を持っている場合は、私に知らせてください:

exiting := false
for !exiting || len(out)>0 {
    select {
        case i := <-out:
            fmt.Printf("Value: %d\n", i)
        case <-exit:
            exiting = true
            fmt.Println("Exiting")
    }
}

受信時に終了する代わりに、出口にフラグを立て、何も残っていないことを確認したら終了しchan outます。

于 2012-06-20T12:30:47.610 に答える