在函数中传递通道的不同方式

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

Different ways to pass channels as arguments in function

问题

我正在阅读一些Go代码,并看到了几种不同的传递Go通道的方式。也许它们是相同的,但我想知道它们是否有任何区别,因为我在网上找不到相关的文档:

func serve(ch <-chan interface{}){ //do stuff }
func serve(ch chan<- interface{}){ //do stuff }
func serve(ch chan interface{}){ //do stuff }
func server(ch *chan interface{}){ //do stuff}

我想知道它们之间的区别是什么,它们是否只是实现相同目的(在不同的goroutine之间传递通道)的等效方式。

注意:我知道没有理由传递指向chan、map、slice或函数值的指针,因为它们都是引用类型,内部包含一个指针(例外情况是如果你希望被调用者更改引用类型的头部)。我提供它的唯一原因是为了完整性(即真正提供每种尝试将通道作为参数传递的方式,并进行比较的问题)。

英文:

I was reading some go code and say a few different ways to pass go channels. Maybe they are the same but I was wondering if there is any difference since I couldn't find documentation online:

1)

func serve(ch <-chan interface{}){ //do stuff }

2)

func serve(ch chan<- interface{}){ //do stuff }

3)

func serve(ch chan interface{}){ //do stuff }

4)

func server(ch *chan interface{}){ //do stuff}

I was wondering what the difference between them were and if they were just equivalent ways to do the same thing: pass a channel around different goroutines.

NOTE: I am aware that there is no reason to pass a pointer to a chan, map, or slice, or function value, since those are all reference types which internally contain a pointer (the exception would be if you want the callee to change the reference type header). The only reason I provided it is for completeness (i.e. to really provide every way a channel could be attempted to be passed as a parameter and to make on question that hopefully, references all ways to do this and compares them).

答案1

得分: 99

我总是建议在可能的情况下传递方向,例如:

func serve(ch <-chan SomeType) { /*do stuff*/ }

func serve(ch chan<- SomeType) { /*do stuff*/ }

通过包含箭头<-chanchan<-,你可以实现三个目标:

  • 你清楚地表明参数是通道的一个端点
  • 你明确地表达了正在提供的哪个端点。chan<-是发送(写入)端,<-chan是接收(读取)端。
  • 你为编译器提供了更多的检查信息。如果函数体尝试使用通道的错误端点,编译器可以引发错误。

这些都是在可能的情况下显示通道端点的好理由。

你的第三种情况是指定通道的端点。这允许访问通道的两个端点,在某些情况下是正确的,但在其他情况下可能会导致意外错误。

第四种情况,传递通道的指针,相当不寻常,也许有点奇怪。如果你想要改变通道,将其作为返回参数更清晰一些。

英文:

I always recommend that the direction is passed everywhere it is possible, e.g.

func serve(ch &lt;-chan SomeType) { /*do stuff*/ }

func serve(ch chan&lt;- SomeType) { /*do stuff*/ }

By including the arrow &lt;-chan or chan&lt;-, you are achieving three things:

  • You are making it clear that the parameter is one end of a channel.
  • You are expressing clearly which end is being supplied. chan&lt;- is the sending (writing) end. &lt;-chan is the receiving (reading) end.
  • You are giving more information to the compiler for checking. If the function body tries to use the wrong end of the channel, the compiler can raise an error.

These are good reasons to show the channel end whenever possible.

Your third case depicts not specifying the end of the channel. This allows both ends of the channel to be accessed, which will be correct in some cases but in other cases may lead to accidental errors.

The fourth case, passing a pointer to a channel, is quite unusual and perhaps a bit odd. If you wanted to change the channel, it would be clearer to include it as a return parameter instead.

答案2

得分: 50

经验法则:箭头表示数据是进入(输出)通道还是离开(输入)通道。没有箭头的是通用通道。

chan <-          写入通道(输出通道)
<- chan          从通道读取(输入通道)
chan             从通道读取或写入(输入/输出通道)
英文:

The rule of thumb: Arrow shows if the data is going into (output) or going out of (input) channel. No arrow is general purpose channel.

chan &lt;-          writing to channel (output channel)
&lt;- chan          reading from channel (input channel)
chan             read from or write to channel (input/output channel)

答案3

得分: 42

这些是不同的通道类型。请参考http://golang.org/ref/spec#Channel_types。关于指针的部分:这种情况比较少见,但如果你想在函数内部改变通道的话可能会有用(在实际应用中很少见到)。

英文:

These are different types of channels. See http://golang.org/ref/spec#Channel_types . For the pointer stuff: Uncommon, but might be useful if you want to change the channel from inside the function (never saw that in the wild).

答案4

得分: 3

从文档中可以看到:channel_types

chan T          // 可以用于发送和接收类型为 T 的值
chan<- float64  // 只能用于发送 float64 类型的值
<-chan int      // 只能用于接收 int 类型的值

示例:

// 可以用于发送和接收
func Bidirectional(c chan any) {
	receive := <-c
	fmt.Println(receive)
	c <- "发送或写入..."
}

// 只能用于接收
func ReceiveOnly(c <-chan any) {
	receive := <-c
	fmt.Println(receive)
	// c <- "" // 错误,因为它只能用于接收而不能发送
}

// 只能用于发送类型为 "any" 的值
func SendOnly(c chan<- any) {
	// receive := <-c // 错误,因为它只能用于发送而不能接收
	c <- "消息..."
}

<kbd>go-playground</kbd>

英文:

From the docs: channel_types

chan T          // can be used to send and receive values of type T
chan&lt;- float64  // can only be used to send float64s
&lt;-chan int      // can only be used to receive ints

Example:

// can be used to send and receive
func Bidirectional(c chan any) {
	receive := &lt;-c
	fmt.Println(receive)
	c &lt;- &quot;sending or writing...&quot;
}

// can only be used to receive
func ReceiveOnly(c &lt;-chan any) {
	receive := &lt;-c
	fmt.Println(receive)
	// c &lt;- &quot;&quot; // error. since it only be used to receive not send
}

// can only be used to send of type &quot;any&quot;
func SendOnly(c chan&lt;- any) {
	// receive := &lt;-c       // error. since it only be used to send not receive
	c &lt;- &quot;Msg...&quot;
}

<kbd>go-playground</kbd>

huangapple
  • 本文由 发表于 2014年7月21日 23:26:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/24868859.html
匿名

发表评论

匿名网友

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

确定