在Golang中,缓冲通道的并发读取可能会导致冲突。

huangapple go评论85阅读模式
英文:

Collision on concurrent read on buffered channel in golang?

问题

我有一个带有缓冲区的通道,被多个(在这个例子中是4个)Go协程读取。

queue := make(chan string, 10000) // 一个大的缓冲通道

每个Go协程都会检查通道中可用的元素数量,并处理它们。

for i := 0; i < 4; i++ { // 启动4个Go协程
    go func() {
        for {
            for elem := range queue {
                // 使用通道中的元素做一些操作
            }
        }
    }()
}

多个Go协程会在读取时发生冲突吗?换句话说,不同的Go协程是否会获取通道中的相同元素,或者当一个Go协程正在读取缓冲区时,其他Go协程是否已经读取和处理了一些元素?如何阻止其他Go协程在一个Go协程正在读取时进行读取?

英文:

I have a buffered channel that are read by multiple (4 in this example) go routines.

queue := make(chan string, 10000) // a large buffered channel

Each go routine checks the number of elements available in the channel and process them all.

for i :=0; i&lt; 4; i++{ // spun 4 go routines
    go func() {
        for {
            for elem := range queue {
                // do something with the elem from the channel
            }
         }
     }
  }

Will multiple go routines collide on the reads? In other words, could different go routine grab the same elem in the channel, or while one go routine is reading the buffer, the other go routines already read and processed some of the elements? How to block other go routines from reading while one go routine is reading?

答案1

得分: 8

简单回答:不行。放置在 Go 通道上的元素只能被读取一次,无论有多少个 goroutine 同时尝试从通道中读取,无论通道是否带有缓冲。除非该元素被多次发送到通道中,否则不可能由两个不同的 goroutine 读取该元素。关于通道语义,缓冲的唯一作用是消除读取和写入必须同步进行的必要性。

英文:

Simple answer: no. Elements placed on a Go channel can only be read once, regardless of how many goroutines are trying to read off the channel at the same time, and that applies regardless of whether the channel is buffered or not. There's no possibility that an element will be read by two different goroutines unless that element was sent to the channel more than once. The only thing that buffering does, with regards to channel semantics, is remove the necessity for the read and write to occur synchronously.

答案2

得分: 1

换句话说,不同的Go协程能否同时获取通道中的相同元素?或者当一个Go协程正在读取缓冲区时,其他Go协程已经读取并处理了一些元素?

不可以...

我认为你对非阻塞和线程安全的概念有一些误解。

非阻塞(带缓冲)通道

> 向带缓冲的通道发送数据只有在缓冲区满时才会阻塞。

带缓冲的通道有一个缓冲区,用于存储一定数量的元素。它允许读取的Go协程在写入的Go协程将元素放入通道之前进行读取,前提是通道中已经有一些元素被写入。如果通道是无缓冲的,它只能包含一个元素,因此在写入一个元素之前,需要阻塞通道。“阻塞/非阻塞”概念与“线程安全”概念无关,非阻塞并不意味着不线程安全。

Go通道的线程安全性

在所有可用的使用方式中,Go通道都是线程安全的。通道是引用类型,因此一旦使用make分配了通道,通道就可以通过值传递,因为它隐含地指向单个内存槽。显然,通道中包含的元素永远不会被复制,也不能被读取两次。

英文:

> In other words, could different go routine grab the same elem in the channel, or while one go routine is reading the buffer, the other go routines already read and processed some of the elements?

Nope...

I believe a misunderstanding is in difference between non-blocking and thread safe concepts.

Non blocking (buffered) channels

> Sends to a buffered channel block only when the buffer is full.

Buffered channels <strike>just like it said</strike> have buffers to store some amount of items. It allows reading goroutine to read without await for writing goroutine put an item to a channel on condition something already written to a channel. If a channel unbuffered it can contain just single item therefore it requires to block channel for writing before a written item be withdrawn. "Blocking/non-blocking" concept doesn't related to "thread safe" concept and non-blocking doesn't mean not thread safe.

Thread safety of Go channels

Go channels are thread safe in all available ways of use. Channel is a reference type so once allocated with make channel could be passed by value because it has implicit pointer to a single memory slot. Obviously contained in a channel item never be copied and couldn't be read twice.

huangapple
  • 本文由 发表于 2017年3月3日 05:15:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/42566116.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定