Go 的 channel 使用非常方便,但是总听说 channel 会拷贝传递的数据,生怕频繁拷贝影响效率。
究竟是怎么个拷贝法呢,下面会有两个 demo 验证下。
先说结论:
Go channel 的发送接收数据的拷贝和 Go 的函数传参道理是一样的,都是默认的值拷贝。
如果你传递一个值,那么 Go 会复制一份新的;如果传递一个指针,则会拷贝这个指针,不会去拷贝这个指针所指的变量(这一点 C++ 选手可能会理解比较深)。
所以,如果你需要通过 channel 传递一个很大的 struct ,那么应该传递 指针。但是,要非常注意通过 channel 发送后,不要修改这个指,这会导致线程间潜在的竞争。
下面是两个验证的小 demo:
- 通过 channel 传递指针
package main
import (
"fmt"
"time"
)
func recv(ch <-chan *int) {
time.Sleep(1 * time.Second)
out := <-ch
fmt.Println("recv : ", out, *out)
}
func main() {
i := 1
ch := make(chan *int, 2)
fmt.Println("i : ", &i, i)
go recv(ch)
ch <- &i
i = 2
time.Sleep(2 * time.Second)
fmt.Println("i : ", &i, i)
}
输出:
i : 0xc000084000 1
recv : 0xc000084000 2
i : 0xc000084000 2
上面的代码通过 channel 发送了 *int 的数据,在接收的协程中先 sleep 1 秒钟让别的协程去更改传递的值。
从打印结果可以看出,通过 channel 接收的数据,只是拷贝了对象的地址而已。
- 通过 channel 传递值
package main
import (
"fmt"
"time"
)
func recv(ch <-chan int) {
time.Sleep(1 * time.Second)
out := <-ch
fmt.Println("recv : ", &out, out)
}
func main() {
i := 1
ch := make(chan int, 2)
fmt.Println("i : ", &i, i)
go recv(ch)
ch <- i
i = 2
time.Sleep(2 * time.Second)
fmt.Println("i : ", &i, i)
}
输出:
i : 0xc00008e000 1
recv : 0xc00007e008 1
i : 0xc00008e000 2
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于