Golang多频道写入/接收订购

时间:2018-05-08 16:33:46

标签: go

我的具体问题是我有一个无缓冲的频道,并且正在生成多个与信号量绑定的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核心内的某个位置?

1 个答案:

答案 0 :(得分:3)

没有定义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来消除此问题,但如果没有明确同步,则无法保证"happens before"关系。

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

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