简体   繁体   中英

Golang Multiple Channel Write/Receive Ordering

My specific issue is that I have an unbuffered channel and am spawning multiple goroutines bounded with a semaphore to perform work:

func main() {
    sem := make(chan struct{}, 10) // allow ten concurrent parsers
    wg := &sync.WaitGroup{}
    wg.Add(1)
    DoSomething("http://example.com", sem, wg)

    wg.Wait()
    // all done
}

func DoSomething(u string, sem chan struct{}, wg *sync.WaitGroup) {
    defer wg.Done()

    sem <- struct{}{}        // grab
    defer func() { <-sem }() // release


    var newSomethings []string

    // ...

    for u := range newSomethings {
        wg.Add(1)
        go DoSomething(u)
    }
}

If there are multiple DoSomething goroutines on the stack, blocked on the sem write (or inversely on a read) When a write happens is there any ordering to which go routine gets through with the write?? I would guess it were random but I could imagine:

  • it is random
  • writes/receives happen in the order they are registered
  • implementation dependent

I looked at a couple of resources and was unable to find a solution:

I'm wondering if this is undefined and/or implementation dependent, or if this logic is located and defined somewhere within go core?

The order that goroutines blocked on a send operation are serviced is not defined, but it's implemented as a FIFO. You can see the implementation in runtime/chan.go , which uses a linked list to track the channel's senders and receivers.

We can try to make an example showing the effective ordering like so:

func main() {
    ch := make(chan int)
    ready := make(chan int)

    for i := 0; i < 10; i++ {
        i := i
        go func() {
            ready <- 1
            ch <- i
        }()
        <-ready
        runtime.Gosched()
    }

    for i := 0; i < 10; i++ {
        v := <-ch
        if i != v {
            panic("out of order!")
        }
        fmt.Println(v)
    }
}

https://play.golang.org/p/u0ukR-5Ptw4

This still isn't technically correct, because there's no way to observe blocking on a send operation, so there's still a race between the ready send and the send to ch on the next line. We can try to eliminate that with the runtime.Gosched call here, or even a time.Sleep , but without explicit synchronization there's no guarantee of a "happens before" relationship.

Regardless, this queues up the goroutines and shows the expected output order, and if they weren't queued up already, it would be more likely to process the values out of order.

You can see by this example that we can't truly determine the order that the goroutines are queued up, it is almost always non-deterministic, and therefore reasoning about this isn't usually useful in practice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM