目录
一、不安全编程
1、使用场景
主要是和外部的C程序库进行交互时,其它场景使用较少。
2、不安全行为的危险性
虽然 Go语言不支持强制类型转换,但是一旦我们使用不安全指针 unsafe.Pointer() 把它的地址拿出来之后,我们就可以把它转换为任何类型的指针。
package unsafe_programming
import (
"testing"
"unsafe"
)
//用 unsafe.Pointer() 做数据类型强制转换
func TestUnsafe(t *testing.T) {
var num int = 10
numAddr := unsafe.Pointer(&num)
t.Log(numAddr)
//用不安全指针拿到 num 的地址,并转换为 float64 类型
f := *(*float64)(unsafe.Pointer(&num))
t.Logf("type: %T, value: %v", f, f)
}
/*
=== RUN TestUnsafe
unsafe_test.go:12: 0xc00001e2a8
unsafe_test.go:16: type: float64, value: 5e-323
--- PASS: TestUnsafe (0.00s)
PASS
*/
通过上面的代码我们可以看出,我们原本想将 整数 10 转换为 64 位浮点数,但是最终的结果却是 5e-323 ,与我们预期的不符,所以在使用 unsafe 时一定要特别注意,这种转换是非常非常危险的。
3、合理的使用 unsafe.Pointer()
当然,在某些情况下我会还是可以考虑用 unsafe.Pointer() 的,比如下面这种数据类型相同的场景。
package unsafe_programming
import (
"testing"
"unsafe"
)
//自定义一个数据类型
type MyInt int
//相同数据类型之间可以做数据类型转换
func TestConvert(t *testing.T) {
s1 := []int{1, 2, 3, 4}
s2 := *(*[]MyInt)(unsafe.Pointer(&s1))
t.Logf("type: %T, value: %v", s2, s2)
}
/*
=== RUN TestConvert
unsafe_test.go:37: type: []unsafe_programming.MyInt, value: [1 2 3 4]
--- PASS: TestConvert (0.00s)
PASS
*/
4、Atomic原子类型操作
atomic 提供了一个对指针的原子操作,这个指针的原子操作经常会在读写一个并发缓存时用到,为了达到读和写的线程安全,我们在写的时候先写到一个 共享的buffer 里面,当我们写完后,我们在用一个原子操作,把读的位置重新指向我们新写好的那个buffer的地址。那么这个切换的过程就需要用 atomic 来保证线程安全。
package unsafe_programming
import (
"fmt"
"sync"
"sync/atomic"
"testing"
"unsafe"
)
//atomic原子类型操作
func TestAtomic(t *testing.T) {
var shareBufPtr unsafe.Pointer
writeDataFn := func() {
data := []int{}
for i := 0; i < 10; i++ {
data = append(data, i)
}
//数据写完后,用一个原子操作,把它放入共享的 buffer
atomic.StorePointer(&shareBufPtr, unsafe.Pointer(&data))
}
readDataFun := func() {
//用原子操作,去共享 buffer 里面取数据
data := atomic.LoadPointer(&shareBufPtr)
fmt.Println(data, *(*[]int)(data))
}
var wg sync.WaitGroup
//因为下面是协程并发读取数据,所有需要先写点儿数据进去,
//否则会报错: invalid memory address or nil pointer dereference
writeDataFn()
//分别开5条写协程和5条读协程,看数据是否能正常读写
for j := 0; j < 5; j++ {
wg.Add(1)
go func() {
writeDataFn()
wg.Done()
}()
wg.Add(1)
go func() {
readDataFun()
wg.Done()
}()
}
wg.Wait()
}
/*
=== RUN TestAtomic
0xc000130030 [0 1 2 3 4 5 6 7 8 9]
0xc000130030 [0 1 2 3 4 5 6 7 8 9]
0xc000130078 [0 1 2 3 4 5 6 7 8 9]
0xc00008e000 [0 1 2 3 4 5 6 7 8 9]
0xc00008e030 [0 1 2 3 4 5 6 7 8 9]
--- PASS: TestAtomic (0.00s)
PASS
*/
我们可以看出,经过 atomic 进行原子操作后,多协程最终的读写结果都正常。
二、总结
- unsafe主要用在外部的C程序库进行交互。
- 不安全指针 unsafe.Pointer() 把它的地址拿出来之后,我们就可以把它转换为任何类型的指针,但是要注意这种转换是非常非常危险,只能用在同类型间,不同类型直接不要用。
- atomic.StorePointer() 用来将数据存放到一个 unsafe 指针变量里面。
- atomic.LoadPointer() 用来从 unsafe 指针变量里面读取数据。
注:这篇博文是我学习中的总结,如有转载请注明出处:
https://blog.csdn.net/DaiChuanrong/article/details/118771036
上一篇:Go-反射编程
下一篇: