我有一片频道都收到同样的信息:
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在写入时被阻止的发送顺序。
在频道解锁后,是否有关于发送顺序的保证?
答案 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都被“触发”在同一个复杂的循环中,没有进一步的同步,所以即使在他们开始发送消息之前,我们根本不知道哪些将首先执行。
然而,这提出了一个合理的问题:如果我们以某种方式保证几个阻止发送指令的顺序,我们是否保证以相同的顺序接收它们?
发送的“发生在之前”属性很难创建。我担心这是不可能的,因为:
例如,如果我有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)
当然,这些方法会因同步而删除所有并行。然而, 您的计划的并发方面仍然存在。