Go 围棋频道被封锁时是否保持秩序?

Go 围棋频道被封锁时是否保持秩序?,go,channel,goroutine,Go,Channel,Goroutine,我有一段通道,所有通道都接收相同的消息: func broadcast(c <-chan string, chans []chan<- string) { for msg := range c { for _, ch := range chans { ch <- msg } } } 但是,传递到每个通道的消息的顺序很重要。我查看了规范,以查看通道在被阻止时是否保持顺序,我发现的是: 如果容量大于零,则通道

我有一段通道,所有通道都接收相同的消息:

func broadcast(c <-chan string, chans []chan<- string) {
    for msg := range c {
        for _, ch := range chans {
            ch <- msg
        }
    }
}
但是,传递到每个通道的消息的顺序很重要。我查看了规范,以查看通道在被阻止时是否保持顺序,我发现的是:

如果容量大于零,则通道是异步的:如果缓冲区未满(发送)或不为空(接收),并且元素按发送顺序接收,则通信操作将在不阻塞的情况下成功

对我来说,如果写入被阻止,那么它不是“已发送”,而是等待发送。有了这个假设,上面没有提到当多个goroutine在写入时被阻止时的发送顺序


通道解除阻塞后,发送顺序是否有任何保证?

没有,没有任何保证


即使在通道未满的情况下,如果两个goroutine几乎同时启动以发送给它,我认为也不能保证首先启动的goroutine实际上会首先执行。因此,您不能指望消息按顺序到达。

如果频道已满,您可以删除消息(然后设置一个标志以暂停客户端并向其发送消息,说明他们正在删除消息或其他内容)

类似于(未经测试的):

类型客户端结构{
名称字符串

ch chan在本规范中,无任何保证

给定示例代码的主要问题不在于通道行为,而在于大量创建的goroutine。所有goroutine都是在同一个叠瓦循环中“激发”的,没有进一步的同步,因此即使在它们开始发送消息之前,我们也不知道哪些goroutine将首先执行

然而,这通常会引起一个合理的问题:如果我们以某种方式保证几个阻塞发送指令的顺序,我们是否保证以相同的顺序接收它们

发送的“之前发生”属性很难创建。我担心这是不可能的,因为:

  • 在发送指令之前,任何事情都可能发生:例如,其他goroutine是否执行自己的发送
  • 在发送中被阻止的goroutine无法同时管理其他类型的同步

  • 例如,如果我有10个编号为1到10的goroutine,我没有办法让他们同时以正确的顺序将自己的编号发送到频道。我所能做的就是使用各种顺序技巧,比如在一个goroutine中进行排序。

    这是对已发布答案的补充

    实际上,每个人都说,问题在于戈罗廷的执行顺序, 您可以通过传递 要运行的goroutine:

    func coordinated(coord chan int, num, max int, work func()) {
        for {
            n := <-coord
    
            if n == num {
                work()
                coord <- (n+1) % max
            } else {
                coord <- n
            }
        }
    }
    
    coord := make(chan int)
    
    go coordinated(coord, 0, 3, func() { println("0"); time.Sleep(1 * time.Second) })
    go coordinated(coord, 1, 3, func() { println("1"); time.Sleep(1 * time.Second) })
    go coordinated(coord, 2, 3, func() { println("2"); time.Sleep(1 * time.Second) })
    
    coord <- 0
    
    func协调(协调变量、数值、最大值、工作函数()){
    为了{
    
    n:=嗯,很有意思。如果我像问比约恩·汉森(Bjorn Hansen)建议的那样做了什么,但在默认情况下像我的例子中那样启动了一个goroutine呢?仍然不能保证吗?杰米森:如果频道已满,你希望发生什么?如果你不想删除消息,为什么不让频道更长?如果你不想删除消息,但不想o知道客户机落后多远,然后制作一个“另一种方式”频道,返回消息编号或类似的信息,这样你就可以知道哪个消息已被接收/发送到套接字/无论你做什么。@AskBjørnHansen-我正在写一个IRC弹跳器,所以我想删除最旧的。如果len(ch)=cap(ch),我可能只需要做
    {我会按照我建议的方式来做,因为规范中记录了len()和cap()总是返回0(我不知道他们没有)。不确定这是代码中的疏忽还是规范中的疏忽:啊哈,我问了golang nuts,我误读了规范;len()和cap()在通道上工作,只是确保在检查长度时没有竞争条件(要么只有一个goroutine写入通道,要么用互斥锁保护写入)。我建议仔细地将goroutine中使用的变量包装(捕获)到参数中:go func(ch2对实际答案没有进一步的评论(已经很好地解释了)。我只是想指出,有一个包实现了一个环缓冲通道,所以你不必编写自己的代码来删除旧消息:是的,这个问题就是我想要解决的。我可以设计一个使用sleeps的示例,但似乎无论如何也不能保证。我猜通道不是sched的特例uler,这是@andybalholm似乎暗示的。+1这解决了我的实际问题,但并没有真正回答我的问题。谢谢!我忘记了
    select
    和完整频道,这迫使我审查了规范并找到了一些其他好东西。我还没有测试,但我想你不能保证goroutine将运行预期数量的。我将在“func”中添加“sleep”在coordYes之后,我不能确定具有该数字的goroutine是否会运行。但是,我可以在适当的时候强制执行执行执行实际工作的函数。第一种解决方案是一些猜测,而第二种解决方案保证按顺序调用函数。
    type Client struct {
        Name string
        ch   chan<-string
    }
    
    func broadcast(c <-chan string, chans []*Client) {
        for msg := range c {
            for _, ch := range chans {
                select {
                case ch.ch <- msg:
                // all okay
                default:
                    log.Printf("Channel was full sending '%s' to client %s", msg, ch.Name)
                }
            }
        }
    }
    
    func coordinated(coord chan int, num, max int, work func()) {
        for {
            n := <-coord
    
            if n == num {
                work()
                coord <- (n+1) % max
            } else {
                coord <- n
            }
        }
    }
    
    coord := make(chan int)
    
    go coordinated(coord, 0, 3, func() { println("0"); time.Sleep(1 * time.Second) })
    go coordinated(coord, 1, 3, func() { println("1"); time.Sleep(1 * time.Second) })
    go coordinated(coord, 2, 3, func() { println("2"); time.Sleep(1 * time.Second) })
    
    coord <- 0
    
    func executor(funs chan func()) {
        for {
            worker := <-funs
            worker()
            funs <- worker
        }
    }
    
    funs := make(chan func(), 3)
    
    funs <- func() { println("0"); time.Sleep(1 * time.Second) }
    funs <- func() { println("1"); time.Sleep(1 * time.Second) }
    funs <- func() { println("2"); time.Sleep(1 * time.Second) }
    
    go executor(funs)