简体   繁体   English

Golang多通道写入/接收排序

[英]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: 我的具体问题是,我有一个无缓冲的通道,并且正在生成多个带有信号量的goroutine来执行工作:

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?? 如果堆栈上有多个DoSomething goroutine,在sem写操作(或在读操作上相反)中被阻塞,则在发生写操作时,go例程对写操作的处理顺序如何? 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? 我想知道这是否是未定义的和/或实现相关的,或者是否在go内核中的某个位置找到并定义了此逻辑?

The order that goroutines blocked on a send operation are serviced is not defined, but it's implemented as a FIFO. 未定义发送操作中阻塞的goroutine的服务顺序,但将其实现为FIFO。 You can see the implementation in runtime/chan.go , which uses a linked list to track the channel's senders and receivers. 您可以在runtime / chan.go中看到实现,该实现使用链接列表跟踪通道的发送者和接收者。

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 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. 从技术上来说,这仍然是不正确的,因为无法观察到发送操作上的阻塞,因此在下一行上的ready发送与发送到ch之间仍然存在竞争。 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. 我们可以尝试使用此处的runtime.Gosched调用,甚至time.Sleep消除这种time.Sleep ,但是如果没有显式同步,就无法保证“先发生”关系。

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. 无论如何,这都会使goroutine排队,并显示预期的输出顺序,如果尚未将它们排队,则更有可能乱序处理这些值。

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. 通过此示例可以看到,我们无法真正确定goroutine排队的顺序,它几乎总是不确定的,因此在实践中通常没有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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