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)