最近忽发奇想,在Golang中怎么把,已经关闭的管道再次打开。这样就避免了,每次都要make一个新的chan,节省内存申请和GC的时间,查看go的源代码,经过简单地摸索后,实现了,示例代码如下。
func TestUnsafe(t *testing.T) {
c1 = make(chan struct{}, 10)
c1 <- struct{}{}
<-c1
close(c1)
p := (*unsafe.Pointer)(unsafe.Pointer(&c1))
c := (*hchan)(*p)
c.closed = 0//打开被关闭的chan
c1 <- struct{}{}
<-c1
}
func TestSafe(t *testing.T) {
c1 = make(chan struct{}, 10)
c1 <- struct{}{}
<-c1
close(c1)
p := (*unsafe.Pointer)(unsafe.Pointer(&c1))
c := (*hchan)(*p)
atomic.CompareAndSwapUint32(&c.closed, 1, 0) //打开被关闭的chan
c1 <- struct{}{}
<-c1
}
最终代码 发布到 github.com/yireyun/go-openc,有兴趣的朋友可以看看。
这种做法最大的缺点是如果go的runtime的数据结构发生变化,将不再可行。因此增加了在init方法中检测的go内核数据结构是否发生变化的代码,性能测试结果如下。
BenchmarkMakeCloseChan-4 20000000 90.9 ns/op
--- BENCH: BenchmarkMakeCloseChan-4(每次新建在关闭的性能)
go1.7.4, Times: 1, use: 0s 0s/op
go1.7.4, Times: 100, use: 0s 0s/op
go1.7.4, Times: 10000, use: 998.3µs 99ns/op
go1.7.4, Times: 1000000, use: 91.0664ms 91ns/op
go1.7.4, Times: 20000000, use: 1.8185449s 90ns/op
BenchmarkCloseOpenChan-4 50000000 29.5 ns/op
--- BENCH: BenchmarkCloseOpenChan-4(非安全打开的性能)
go1.7.4, Times: 1, use: 0s 0s/op
go1.7.4, Times: 100, use: 0s 0s/op
go1.7.4, Times: 10000, use: 0s 0s/op
go1.7.4, Times: 1000000, use: 28.5199ms 28ns/op
go1.7.4, Times: 50000000, use: 1.473497s 29ns/op
BenchmarkCloseOpenChanSync-4 30000000 44.9 ns/op
--- BENCH: BenchmarkCloseOpenChanSync-4(安全打开的性能)
go1.7.4, Times: 1, use: 0s 0s/op
go1.7.4, Times: 100, use: 0s 0s/op
go1.7.4, Times: 10000, use: 498.2µs 49ns/op
go1.7.4, Times: 1000000, use: 47.5278ms 47ns/op
go1.7.4, Times: 30000000, use: 1.3479236s 44ns/op
PASS
ok github.com/yireyun/go-openc 8.235s
有疑问加站长微信联系(非本文作者)