Golang中的结构体中的通道:当传递给函数时,它可以是有方向性的吗?

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

Golang channel in a struct: Can it be directional when passed to a function?

问题

当将通道传递给函数时,我知道你可以指定通道可以用于的方向;例如,

func MyFunc(ch chan<- string) {
    ch <- "Hello"
}

"ch" 只能由 MyFunc 用于向其他地方的接收器发送字符串,而 MyFunc 不能监听来自 ch 的消息。

为了简化创建动态数量的通道以供一些 goroutine 使用,我创建了一个包含通道的结构体。

type ChanStruct struct {
    chMessages chan string
}

然后我实例化了一些结构体:

var slcChanStruct []ChanStruct
for a := 0; a <= 2; a++ {
    var tmpChanStruct ChanStruct
    tmpChanStruct.chMessages = make(chan string)
    slcChanStruct = append(slcChanStruct, tmpChanStruct)
}

现在我有了 3 个结构体,我可以通过遍历结构体切片来单独读取/写入通道。但是当我将它们发送到 goroutine 时:

for a := 0; a <= 2; a++ {
    go SomeFunc(slcChanStruct[a])
}

...有没有办法通过指定 goroutine 只能使用 ChanStruct.ch 通道来增加一些安全性?

注意,我这样做的原因是,如果我不知道在并行进程集中需要多少个通道(进程的数量作为命令行参数传递),使用带有通道的结构体意味着我可以创建一个可以传递给任意数量的 goroutine 并直接通过遍历它们来单独访问它们的切片;之前,如果我想要从多个 goroutine 中单独读取,我必须创建一组固定数量的通道。我想通过指定进程只能以某种方式使用通道来增加一些安全性,就像在使用单独的通道时那样(单独的通道意味着我可以告诉特定的 goroutine 退出而不被其他 goroutine 拦截)。如果我不能在使用结构体中的通道的函数中添加方向性,是否有更符合惯用法的方法来实现这一点?

英文:

When passing a channel to a function, I know you can specify the direction that channel may use the channel for; for example,

func MyFunc(ch chan&lt;- string) {
    ch &lt;- &quot;Hello&quot;
}

"ch" can only be used by MyFunc to send strings to a receiver elsewhere and MyFunc cannot listen for messages from ch.

To simplify creating a dynamic number of channels to some number of goroutines, I created a struct containing channels.

type ChanStruct struct {
    chMessages chan string
}

Then I instantiate a structs:

var slcChanStruct []ChanStruct
for a:= 0; a &lt;=2; a++ {
    var tmpChanStruct ChanStruct
    tmpChanStruct.chMessages = make(chan string)
    slcChanStruct = append(slcChanStruct, tmpChanStruct)
}

Now I have 3 structs I can individually read/write with a channel by ranging over a slice of structs. But when I send them off to goroutines:

for a:=0; a &lt;=2; a++{
    go SomeFunc(slcChanStruct[a])
}

...is there a way to add a bit of safety by specifying that the goroutines can only, for example, send using the ChanStruct.ch channel?

NOTE the reason I was doing this is that if I don't know how many channels I'll need for a parallel set of processes (the number of processes are passed as a command line argument) using a struct with channels means I can create a slice that can be passed to any number of goroutines and directly access them individually by ranging over them; before, I had to create a set number of channels if I wanted to individually read from a number of goroutines. I'd like to add some safety to this by specifying that processes can only use a channel a certain way, like I could when using individual channels (individual channels mean I can do something like telling a particular goroutine to quit without other goroutines intercepting it). Is there a more idiomatic way to do this if I can't add directionality to functions using the channel in a struct?

答案1

得分: 4

在@Adrian的建议之上,如果你需要确保在一个通道上进行单向通信,你可以使用匿名函数:

for a := 0; a <= 2; a++ {
  go func(ch <-string) {
    SomeFunc(ch)
  }(slcChanStruct[a].chMessages)
}

请注意,你需要将通道传递给SomeFunc而不是结构体。

如果你仍然希望在通道上进行双向通信,你可以将通道重新分配给单向类型:

type ChanStruct struct {
    chMessages chan string
}

type ChanStructRecv struct {
    chMessages <-chan string
}

// 更新 SomeFunc 的类型:

func SomeFunc(s ChanStructRecv) {
  // ...
}

最后,循环可以进行类似的修改:

for a := 0; a <= 2; a++ {
  go func(s ChanStruct) {
    var sr ChanStructRecv
    sr.chMessages = s.chMessages
    SomeFunc(sr)
  }(slcChanStruct[a])
}
英文:

On top of @Adrian's suggestions, if you have to ensure one way communication on a channel, you can always use an anonymous function:

for a:=0; a &lt;=2; a++{
  go func f(ch &lt;-string) {
    SomeFunc(ch)
  }(slcChanStruct[a].chMessages)
}

Note that you'll have to pass the channel to SomeFunc instead of the struct.

If you still want to perform both way communication on your channel, you can re assign the channel to a one directional type:

type ChanStruct struct {
    chMessages chan string
}

type ChanStructRecv struct {
    chMessages &lt;-chan string
}

// Update SomeFunc type:

func SomeFunc(s ChanStructRecv) {
  // ...
}

Finally, the loop can be modified similarly:

for a:=0; a &lt;=2; a++{
  go func f(s ChanStruct) {
    var sr ChanStructRecv
    sr.chMessages = s.chMessages
    SomeFunc(sr)
  }(slcChanStruct[a])
}

答案2

得分: 1

有几种方法,取决于你的具体情况:

  • 与其传递结构体,不如传递通道,并将通道参数设置为方向性,就像你第一个示例中那样。
  • 不直接暴露通道,只公开一个用于向其写入的结构体方法(如果没有读取方法,它就是方向性的)。
  • 将通道作为结构体成员设置为方向性。

例如:

type myStruct struct {
    c chan<- bool
}
英文:

A couple of ways, depending on your particular situation:

  • Pass the channel instead of the struct, and make the channel parameter directional as in your first example
  • Don't expose the channel directly, just expose a struct method to write to it (if there's no method to read from it, it's effectively directional)
  • Make the channel struct member directional:

E.g.

type myStruct struct {
    c chan &lt;- bool
}

huangapple
  • 本文由 发表于 2017年6月2日 21:27:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/44330146.html
匿名

发表评论

匿名网友

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

确定