golang fifo 缓冲通道

Posted

技术标签:

【中文标题】golang fifo 缓冲通道【英文标题】:golang fifo buffered channel 【发布时间】:2016-08-30 04:14:19 【问题描述】:

据我了解:当通道已满时,GO 中的缓冲通道不是 FIFO。 我的应用程序中需要这种行为(FIFO 行为)。 我怎样才能实现这种行为?有什么开源的吗? 在此先感谢

编辑: 有些人不喜欢这个问题,所以让我说得更清楚: 我的意思是当缓冲通道已满并且多个发送者被阻止时 在尝试将项目添加到频道时,它们将被释放的顺序 不是先进先出。你也可以阅读这个讨论:https://github.com/golang/go/issues/11506

是的,我一直在寻找实现该行为的第三方库。 抱歉没有说清楚。

【问题讨论】:

正如你刚才所说,这就是 Go 中通道的工作方式。您可以通过使用频道来实现这一点......对不起,但绝对是可怕的问题。 @evanmcdonnal 未指定在完整通道上阻止的发件人的执行顺序。问如何制作这个FIFO并不可怕。规范中没有任何内容表明您可以通过使用频道来实现这一点......问题的问题是它要求推荐一个软件包。 @KarrotKake 不,我认为它很清楚地说明了项目是按照发送顺序从频道中读取的,这意味着先进先出。 @evanmcdonnal 规范和内存模型文档没有说明发送时阻塞的 goroutines 的执行顺序。 我将问题编辑得更清楚。很抱歉从一开始就不是这样 【参考方案1】:

Go 中的缓冲通道始终是 FIFO。规范清楚地说:

通道充当先进先出队列。

如果来自通道的值不是 FIFO,那么这是通道实现中的错误。

以下代码应始终按正确顺序打印 1、2、3、4:

package main

import (
    "fmt"
    "time"
)

func main() 
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3

    go func() 
        ch <- 4
    ()

    time.Sleep(time.Second)

    for i := 0; i < 4; i++ 
        fmt.Println(<-ch)
    

Playground link

请注意,当有多个并发发送者时,不保证先发送哪个值。如果有多个等待发送者并且有人从通道缓冲区中删除了一个元素(或者在无缓冲通道的情况下,尝试从通道接收),运行时将随机选择一个发送 goroutine。

例子:

package main

import (
    "fmt"
    "time"
)

func main() 
    ch := make(chan int, 2)
    ch <- 1

    go func() 
        ch <- 2
    ()

    go func() 
        ch <- 3
    ()

    time.Sleep(time.Second)

    for i := 0; i < 3; i++ 
        fmt.Println(<-ch)
    

Playground link

如果您多次运行此代码,您会看到输出有时会是 1、2、3 或 1、3、2。(这在操场上不起作用,因为输出已缓存)

【讨论】:

我的意思是在有多个发件人的情况下,不能保证第一个被阻止的发件人会第一个被释放。这就是我说“当通道已满时,GO 中的缓冲通道不是 FIFO”时的意思。无论如何,我正在寻找 Fifo 阻止发件人的实现。 @user3142398 如果你想要这个,你必须自己实现它。我不知道有什么包可以做到这一点。您可能可以使用队列和条件变量以及递增计数器进行发送,但它可能不是很有效。【参考方案2】:

你可以使用链表。

容器/列表包 (https://golang.org/pkg/container/) 实现了一个可以用作队列的双向链表。也许,它还实现了 Heap,它允许您创建具有优先级的队列。总之,最简单的方法是链表IHMO:

queue := list.New()

queue.PushBack("Hello ") // Enqueue
queue.PushBack("world!")

for queue.Len() > 0 
    e := queue.Front() // First element
    fmt.Print(e.Value)

    queue.Remove(e) // Dequeue

【讨论】:

以上是关于golang fifo 缓冲通道的主要内容,如果未能解决你的问题,请参考以下文章

在 golang 中创建一片缓冲通道

Golang:为啥增加缓冲通道的大小会消除我的 goroutine 的输出?

Golang入门到项目实战 golang并发变成之通道channel

如何在 Golang 中正确处理缓冲通道?

golang 通道有缓冲区

Go:缓冲通道总和更快?