阻止时,渠道是否保留订单?

时间:2013-04-07 03:47:35

标签: go channel goroutine

我有一片频道都收到同样的信息:

func broadcast(c <-chan string, chans []chan<- string) {
    for msg := range c {
        for _, ch := range chans {
            ch <- msg
        }
    }
}

但是,由于chans中的每个频道都可能以不同的速率被读取,因此当我得到一个慢的消费者时,我不想阻止其他频道。我用goroutines解决了这个问题:

func broadcast(c <-chan string, chans []chan<- string) {
    for msg := range c {
        for _, ch := range chans {
            go func() { ch <- msg }()
        }
    }
}

但是,传递给每个通道的消息顺序很重要。我看了一下规范,看看频道在封锁时是否保留了顺序,而我发现的只是:

  

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

对我而言,如果写入被阻止,则不会“发送”,而是等待发送。有了这个假设,上面没有说明多个goroutine在写入时被阻止的发送顺序。

在频道解锁后,是否有关于发送顺序的保证?

4 个答案:

答案 0 :(得分:6)

不,没有保证。

即使通道未满,如果两个goroutine几乎同时启动发送给它,我认为没有任何保证首先启动的goroutine会先实际执行。所以你不能指望按顺序到达的消息。

答案 1 :(得分:4)

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

(未经测试)的某些内容:

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)
            }
        }
    }
}

答案 2 :(得分:1)

在此代码中,没有保证。

给定示例代码的主要问题不在于通道行为,而在于众多创建的goroutine。所有的goroutine都被“触发”在同一个复杂的循环中,没有进一步的同步,所以即使在他们开始发送消息之前,我们根本不知道哪些将首先执行。

然而,这提出了一个合理的问题:如果我们以某种方式保证几个阻止发送指令的顺序,我们是否保证以相同的顺序接收它们?

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

  1. 在发送指令之前发生任何事情:例如,其他goroutines执行自己的发送
  2. 发送中被阻止的goroutine无法同时管理其他类型的同步
  3. 例如,如果我有10个编号为1到10的goroutines,我无法让他们以正确的顺序同时向频道发送他们自己的号码。我所能做的就是使用各种顺序技巧,例如在1个单一的goroutine中进行排序。

答案 3 :(得分:0)

这是对已发布的答案的补充。

几乎每个人都说过,问题是执行goroutines的顺序, 您可以通过传递数字来轻松地使用通道协调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

或使用以有序方式执行工人的中央goroutine:

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)

当然,这些方法会因同步而删除所有并行。然而, 您的计划的并发方面仍然存在。