为什么定向通道切片无法编译?
Why are slices of directional channels failing to compile?
https://play.golang.org/p/-cHBgiNl0tK
package main
import (
"fmt"
"time"
)
func read(ch ...<-chan int) {
for i := 0; i < len(ch); i++ {
i := i
go func() { fmt.Println(<-ch[i]) }()
}
}
func write(ch ...chan<- int) {
for i := 0; i < len(ch); i++ {
i := i
go func() { ch[i] <- i }()
}
}
func main() {
var maxLen = 10
var ch []chan int
for i := 0; i < maxLen; i++ {
ch = append(ch, make(chan int))
}
read(ch...)
write(ch...)
time.Sleep(10 * time.Second)
}
以上代码段失败并出现以下错误
./prog.go:28:6: cannot use ch (type []chan int) as type []<-chan int in argument to read
./prog.go:29:7: cannot use ch (type []chan int) as type []chan<- int in argument to write
如何让它在 read
和 write
函数中使用通道方向?
尽管 chan int
类型的表达式可分配给 chan<- int
(仅发送通道)或 <-chan int
(仅接收通道)类型的变量,但同样不会'应用于此类类型的切片:
var (
ch chan int
_ <-chan int = ch // ok
_ chan<- int = ch // ok
_ []<-chan int = []chan int(nil) // compilation error
_ []chan<- int = []chan int(nil) // compilation error
)
./prog.go:7:2: cannot use ([]chan int)(nil) (type []chan int) as type []<-chan int in assignment
./prog.go:8:2: cannot use ([]chan int)(nil) (type []chan int) as type []chan<- int in assignment
,不幸的是没有简单的出路。我不建议自己使用 unsafe
包,因为与 unsafe
相关的风险抵消了使用通道方向的好处。我会简单地将参数中的通道方向放到函数 write
和 read
中。 YMMV.
您正在尝试将 []chan int
作为 []<-chan int
传递。看似简单的转换,但实际上是 深度 转换,因为您正在更改容器的 元素类型 。这些转换在 Go 中是不允许的,尽管有一个现有的(已关闭的)提议允许这种通道切片转换:https://github.com/golang/go/issues/41695
这是我过去在类似情况下使用的解决方案:
func readOnlyChannels(slice []chan int) []<-chan int {
return *(*[]<-chan int)(unsafe.Pointer(&slice))
}
func writeOnlyChannels(slice []chan int) []chan<- int {
return *(*[]chan<- int)(unsafe.Pointer(&slice))
}
然后用法:
read(readOnlyChannels(ch)...)
write(writeOnlyChannels(ch)...)
由于通道的只读或只写质量只在编译时携带在类型信息中,而不是在类型的实际数据表示中[需要引用],你可以使用 unsafe
来强制转换。
注意:我不知道 unsafe
转换是否有效有任何明确或隐含的保证,因此您应该创建测试以验证这在您的目标平台上是否有效。但是,在实践中,它似乎有效。它依赖于具有相同(或至少兼容)数据表示的不同方向的通道值。实际上,通道方向性的设计没有理由要求甚至受益于在 运行 时间保持通道方向。
如果你想完全避免 unsafe
,那么你必须像这样进行“长”转换:
func readOnlyChannels(slice []chan int) []<-chan int {
out := make([]<-chan int, len(slice))
for i := range slice {
out[i] = slice[i]
}
return out
}
func writeOnlyChannels(slice []chan int) []chan<- int {
out := make([]chan<- int, len(slice))
for i := range slice {
out[i] = slice[i]
}
return out
}
这里唯一真正的缺点是,当您进行转换时,它将通过将所有内容复制到新的切片中来导致更多分配。使用在您的应用程序中造成最少问题的方法。
支持定向通道的通道通道的替代方法。
package main
import (
"fmt"
"time"
)
func read(inputs chan (<-chan int)) {
for input := range inputs {
go func(input <-chan int) { fmt.Println(<-input) }(input)
}
}
func write(outputs chan chan<- int) {
i := 0
for output := range outputs {
go func(output chan<- int, i int) { output <- i }(output, i)
i += 1
}
}
func main() {
var maxLen = 10
readers := make(chan (<-chan int), 10)
writers := make(chan chan<- int, 10)
for i := 0; i < maxLen; i++ {
ch := make(chan int)
readers <- ch
writers <- ch
}
close(readers)
close(writers)
read(readers)
write(writers)
time.Sleep(10 * time.Second)
}
https://play.golang.org/p/-cHBgiNl0tK
package main
import (
"fmt"
"time"
)
func read(ch ...<-chan int) {
for i := 0; i < len(ch); i++ {
i := i
go func() { fmt.Println(<-ch[i]) }()
}
}
func write(ch ...chan<- int) {
for i := 0; i < len(ch); i++ {
i := i
go func() { ch[i] <- i }()
}
}
func main() {
var maxLen = 10
var ch []chan int
for i := 0; i < maxLen; i++ {
ch = append(ch, make(chan int))
}
read(ch...)
write(ch...)
time.Sleep(10 * time.Second)
}
以上代码段失败并出现以下错误
./prog.go:28:6: cannot use ch (type []chan int) as type []<-chan int in argument to read
./prog.go:29:7: cannot use ch (type []chan int) as type []chan<- int in argument to write
如何让它在 read
和 write
函数中使用通道方向?
尽管 chan int
类型的表达式可分配给 chan<- int
(仅发送通道)或 <-chan int
(仅接收通道)类型的变量,但同样不会'应用于此类类型的切片:
var (
ch chan int
_ <-chan int = ch // ok
_ chan<- int = ch // ok
_ []<-chan int = []chan int(nil) // compilation error
_ []chan<- int = []chan int(nil) // compilation error
)
./prog.go:7:2: cannot use ([]chan int)(nil) (type []chan int) as type []<-chan int in assignment
./prog.go:8:2: cannot use ([]chan int)(nil) (type []chan int) as type []chan<- int in assignment
unsafe
包,因为与 unsafe
相关的风险抵消了使用通道方向的好处。我会简单地将参数中的通道方向放到函数 write
和 read
中。 YMMV.
您正在尝试将 []chan int
作为 []<-chan int
传递。看似简单的转换,但实际上是 深度 转换,因为您正在更改容器的 元素类型 。这些转换在 Go 中是不允许的,尽管有一个现有的(已关闭的)提议允许这种通道切片转换:https://github.com/golang/go/issues/41695
这是我过去在类似情况下使用的解决方案:
func readOnlyChannels(slice []chan int) []<-chan int {
return *(*[]<-chan int)(unsafe.Pointer(&slice))
}
func writeOnlyChannels(slice []chan int) []chan<- int {
return *(*[]chan<- int)(unsafe.Pointer(&slice))
}
然后用法:
read(readOnlyChannels(ch)...)
write(writeOnlyChannels(ch)...)
由于通道的只读或只写质量只在编译时携带在类型信息中,而不是在类型的实际数据表示中[需要引用],你可以使用 unsafe
来强制转换。
注意:我不知道 unsafe
转换是否有效有任何明确或隐含的保证,因此您应该创建测试以验证这在您的目标平台上是否有效。但是,在实践中,它似乎有效。它依赖于具有相同(或至少兼容)数据表示的不同方向的通道值。实际上,通道方向性的设计没有理由要求甚至受益于在 运行 时间保持通道方向。
如果你想完全避免 unsafe
,那么你必须像这样进行“长”转换:
func readOnlyChannels(slice []chan int) []<-chan int {
out := make([]<-chan int, len(slice))
for i := range slice {
out[i] = slice[i]
}
return out
}
func writeOnlyChannels(slice []chan int) []chan<- int {
out := make([]chan<- int, len(slice))
for i := range slice {
out[i] = slice[i]
}
return out
}
这里唯一真正的缺点是,当您进行转换时,它将通过将所有内容复制到新的切片中来导致更多分配。使用在您的应用程序中造成最少问题的方法。
支持定向通道的通道通道的替代方法。
package main
import (
"fmt"
"time"
)
func read(inputs chan (<-chan int)) {
for input := range inputs {
go func(input <-chan int) { fmt.Println(<-input) }(input)
}
}
func write(outputs chan chan<- int) {
i := 0
for output := range outputs {
go func(output chan<- int, i int) { output <- i }(output, i)
i += 1
}
}
func main() {
var maxLen = 10
readers := make(chan (<-chan int), 10)
writers := make(chan chan<- int, 10)
for i := 0; i < maxLen; i++ {
ch := make(chan int)
readers <- ch
writers <- ch
}
close(readers)
close(writers)
read(readers)
write(writers)
time.Sleep(10 * time.Second)
}