在具有渠道的Go案例声明中,封锁在哪里发生?

I am a Go rookie.

I'm looking at this construct:

for {
    select {
        case <-resyncCh:
        case <-stopCh:
            return
        case <-cancelCh:
            return
    }
    if r.ShouldResync == nil || r.ShouldResync() {
        // do stuff
    }
    resyncCh = r.resyncChan()
}

Suppose resyncCh does not have a message on it.

Are all the cases evaluated (blocked on) in parallel? Or is there another path through this I'm not seeing?

I read this as:

  • Block on the resyncCh, the stopCh and the cancelCh chans in parallel waiting for messages
  • If a message is received on resyncCh, we effectively fall through to the r.ShouldResync stuff, but the other blocks on the other chans remain.
  • If a message is received at any point on either the stopCh or the cancelCh chan, return, effectively "disconnecting" from all chans here.

Is that correct?

In direct answer to your questions:

  • Block on the resyncCh, the stopCh and the cancelCh chans in parallel waiting for messages. YES.

  • If a message is received on resyncCh, we effectively fall through to the r.ShouldResync stuff, but the other blocks on the other chans remain. No, they don't remain, you are past the select However, since this loops, you will block again. You could also use the fallthrough keyword to make them block after passing the initial one.

  • If a message is received at any point on either the stopCh or the cancelCh chan, return, effectively "disconnecting" from all chans here. Correct - they would return from this function.

Also, bear in mind what you can do with a default --> https://gobyexample.com/non-blocking-channel-operations

for {
    select {
        case <-resyncCh:
        case <-stopCh:
            return
        case <-cancelCh:
            return
        default:
            fmt.Printf("will keep printing
")
    }
    if r.ShouldResync == nil || r.ShouldResync() {
        // do stuff
    }
    resyncCh = r.resyncChan()
}

update: Another useful pattern, I'm using right now, which takes advantage of this:

select {
case m := <-c:
        handle(m)
case <-time.After(5 * time.Minute):
        fmt.Println("timed out")
}

Here you can wait, blocking, on a channel, but eventually timeout, just using the golang time package. Very succinct and easy to read. Compare that to poll() with timespec values. https://golang.org/pkg/time/#After

select takes first not blocked action and goes to next operation.

Block on the resyncCh, the stopCh and the cancelCh chans in parallel waiting for messages

Yes, waiting for first of them.

If a message is received on resyncCh, we effectively fall through to the r.ShouldResync stuff, but the other blocks on the other chans remain.

Unlike in some other languages fallthrough is explicit in go - you should state it.

If a message is received at any point on either the stopCh or the cancelCh chan, return, effectively "disconnecting" from all chans here.

Exit from the function where the code located. Yes, we do not wait for new messages more.