c:=make(chan int)和c:=make(chan int,1)之间有什么区别?

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

What's the difference between c:=make(chan int) and c:=make(chan int,1)?

问题

我认为它们是相同的,但是在《Go内存模型》中有这样的一句话:如果通道是有缓冲的(例如,c = make(chan int, 1)),那么程序不能保证打印出"hello, world",它可能打印空字符串,崩溃或者执行其他操作。这个说法正确吗?

英文:

I think they're the same, but there is a such words in The go memory model:, if the channel were buffered (e.g., c = make(chan int, 1)) then the program would not be guaranteed to print "hello, world" – it might print the empty string, crash, or do something else. Is this correct?

答案1

得分: 20

虽然Evan是正确的,但我认为提供一个更详细的解释可能会有帮助:

根据Effective Go中的说明,以下两种方式是相同的,都创建了无缓冲通道

ci := make(chan int)            // 无缓冲整数通道
cj := make(chan int, 0)         // 无缓冲整数通道

而使用其他任何值都会创建一个带缓冲通道

ck := make(chan int, 1)         // 带缓冲整数通道

带缓冲通道

使用带缓冲通道时,一个Go协程可以将一个值放入通道(ck <- 42),然后继续执行下一条指令,而无需等待其他协程从通道中读取。除非通道缓冲已满,否则这是成立的。

如果通道已满,发送方的Go协程将等待另一个Go协程从通道中读取值,然后才能将自己的值放入通道中。

无缓冲通道

无缓冲通道没有存储任何数据的空间。因此,为了通过无缓冲通道传递值,发送方的Go协程将阻塞,直到接收方的Go协程接收到该值。

因此,带缓冲通道和无缓冲通道之间肯定存在差异。在内存模型的情况下:

package main
import "fmt"

var c = make(chan int)
var a string

func f() {
    a = "hello, world"
    x := <- c
    fmt.Println(x)
}

func main() {
    go f()
    c <- 0
    print(a)
}

如果你使用了带缓冲通道 var c = make(chan int, 1),那么main() Go协程只会将一个值放入缓冲区,然后继续执行print(a),可能在f() Go协程有机会将a设置为"hello, world"之前。

但在当前的代码中,主Go协程将在c <- 0处阻塞,等待f()接收到该值后才继续执行打印操作,这样我们就可以确定a已经被设置为"hello, world"

带缓冲通道的示例代码链接

无缓冲通道的示例代码链接

英文:

While Evan is right, I thought a longer explanation might be useful:

As stated in Effective Go, the following are the same and gives you unbuffered channels:

ci := make(chan int)            // unbuffered channel of integers
cj := make(chan int, 0)         // unbuffered channel of integers

While having any other value would give you a buffered channel:

ck := make(chan int, 1)         // buffered channel of integers

Buffered channel

With a buffered channel, a Go routine may put a value in the channel ( ck &lt;- 42 ) and then continue on with the next instruction without having to wait for someone to read from the channel. This is true unless the channel buffer is full.

If a channel is full, the Go routine will wait for another Go routine to read from the channel before being able to put its own value there.

Unbuffered channel

An unbuffered channel will have no room to store any data. So in order for a value to be passed over an unbuffered channel, the sending Go routine will block until the receiving Go routine has received the value.

So, there is surely a difference between a buffered and an unbuffered channel. In the memory model case:

package main
import &quot;fmt&quot;

var c = make(chan int)
var a string

func f() {
	a = &quot;hello, world&quot;
	x := &lt;- c
    fmt.Println(x)
}

func main() {
	go f()
	c &lt;- 0
	print(a)
}

if you had a buffered channel var c = make(chan int, 1), the main() Go routine would just put a value in the buffer and then continue on with print(a), maybe before the f() Go routine has had time to set a to &quot;hello, world&quot;.

But in the current code, the main Go routine will block at c &lt;- 0, waiting for f() to receive the value before continuing with printing, and then we know for sure that a is already set to &quot;hello, world&quot;.

PlaygroundLink-WithBuffer

PlaygroundLink-WithoutBuffer

答案2

得分: 4

make(chan int) 生成一个无缓冲通道,make(chan int, 1) 生成一个带有容量为1的缓冲通道。

请参考 http://golang.org/doc/effective_go.html#channels 了解它们之间的区别。

英文:

make(chan int) produces an unbuffered channel, make(chan int, 1) produces a channel with a buffer of 1.

See http://golang.org/doc/effective_go.html#channels for an explanation of the difference.

答案3

得分: 1

第一个创建的是无缓冲通道,而第二个创建的是带缓冲通道。

当尝试在通道上发送一个值时,你可以看到它们之间的区别:

c <- 1

对于无缓冲通道,这个语句会阻塞,直到通道上发生相应的接收操作(即 <-c)。对于带缓冲通道,如果通道的缓冲区有空间来存储值,发送操作可以完成而不阻塞。

在大多数情况下,无缓冲通道是合适的,并且由于其阻塞特性,它们能够快速显示出任何并发问题。然而,在某些情况下,使用缓冲区是有意义的。

就 Go 的内存模型而言,对于带缓冲和无缓冲通道,同样适用“happens before”关系。虽然发送操作在带缓冲通道上不会阻塞等待相应的接收操作,但在接收操作之后运行的代码仍然保证在发送操作之前运行的代码之后执行。

英文:

The first creates an unbuffered channel, while the second creates a buffered channel.

You can see the difference when trying to send a value on the channel:

c &lt;- 1

In the case of an unbuffered channel, this statement will block until a corresponding receive operation on the channel occurs (i.e. &lt;-c). In the case of a buffered channel, the send can complete without blocking if there is space to store the value in the channel's buffer.

In most cases, unbuffered channels are suitable and have the benefit that they'll quickly show up any concurrency problems due to their blocking nature. There are certain cases where a buffer makes sense though.

As far as the Go memory model goes, the same "happens before" relationship is true for both buffered and unbuffered channels. While the send operation doesn't block for a corresponding receive with a buffered channel, code that runs after the receive are still guaranteed to "happen after" code that ran before the send.

huangapple
  • 本文由 发表于 2014年4月23日 09:45:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/23233381.html
匿名

发表评论

匿名网友

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

确定