繁体   English   中英

Golang多通道写入/接收排序

[英]Golang Multiple Channel Write/Receive Ordering

我的具体问题是,我有一个无缓冲的通道,并且正在生成多个带有信号量的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)
    }
}

如果堆栈上有多个DoSomething goroutine,在sem写操作(或在读操作上相反)中被阻塞,则在发生写操作时,go例程对写操作的处理顺序如何? 我想这是随机的,但我可以想象:

  • 这是随机的
  • 写入/接收按照注册的顺序进行
  • 实现依赖

我查看了一些资源,但找不到解决方案:

我想知道这是否是未定义的和/或实现相关的,或者是否在go内核中的某个位置找到并定义了此逻辑?

未定义发送操作中阻塞的goroutine的服务顺序,但将其实现为FIFO。 您可以在runtime / chan.go中看到实现,该实现使用链接列表跟踪通道的发送者和接收者。

我们可以尝试制作一个显示有效排序的示例,如下所示:

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

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

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

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

暂无
暂无

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

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