// 三种阻塞方法
defer func() {
for {
}
}()
defer func() {
select {}
}()
defer func() {
<-make(chan bool)
}()
// 常见坑
/*
1.不定参数是空接口类型时,传接口数组和变参是都可以通过,但是得到的参数不同
2.数组是值传递
3.map遍历是无序的。
4.命名的返回值内,同名的局部变量被屏蔽。
5.recover必须在defer函数中运行,直接调用是无效的
6.mian提前退出,goroutine无法保证完成任务
7.睡眠不能保证goroutine运行完成
8.独占cpu导致其他goroutine饿死, runtime.GOMAXPOROCS(1) runtime.Gosched()
9.goroutine和main是并发不是顺序。
10.defer的for循环闭包引用会导致拿到的都是最后一个变量值
11.for循环内部defer会导致资源延迟释放,可以在for循环内添加一个局部函数来解决延迟问题。
12.切片引用会导致整个数组被锁定。当切片引用大数组的小部分内容时,会导致大数组久久不能释放。解决办法就是克隆数据。
13.空指针和空接口不等价。特别是返回error时,自定义的错误类型更要注意区别。
14.内存地址会发生变化,指针会做同步,但非指针类型的uintptr不会对此变化做同步。同理cgo不能保存go的对象地址。
15.死循环的goroutine无法释放引用的资源,使用context可以使不用的goroutine释放。避免goroutine泄露。
*/
// 面向并发的内存模型
/*
1.一般系统级别的线程固定大小栈:2M;goroutine以2/4K启动,动态伸缩可达1G。
2.go有自己的调度器,以n个线程调度m个goroutine。
3.goroutine是半抢占式的协作调度。发生在用户态。
4.只有当前goroutine发生阻塞是才会导致调度。
5.runtime.GOMAXPROCS(X)控制当前正常非阻塞goroutine的系统线程数目。
6.原子操作:
并发编程中 最小的切不可并行化 的操作
sync.Mutex通过互斥可以保证原子性:麻烦且效率低
sync/atomic
sync.Once:单件模式
7.顺序一致性内存模型:
同步原语确定两个事件的顺序。
channel
sync.Mutex
可通过cannel的缓存大小控制goroutine的并发大小。
*/
// 常见的并发模式
/*
CSP:通讯顺序进程-同步通信
并行一般是简单的大量重复,如GPU对图像的大量并行计算。
并发:并发完全可以顺序执行,只有真正的多核CPU上才有可能着呢张的同时运行。
并发编程中对共享资源的控制:go提倡不要通过共享内存通信,而是通过通信来共享内存。
虽然引用计数这种并发可以通过原子操作很好的实现,但是通过channel来控制访问可以写成更加简洁正确的程序。
生产者消费者模型:直接使用channel一句话完成:生产者只写,消费者range
发布订阅模型
控制并发数:带缓存的channel,而且可以通过获取channel的空闲数来反映并发使用率。
goroutine安全退出:
chan bool来控制goroutine退出,sync.WaitGroup来让main等待goroutine的退出时的清理动作。
context包用于处理单个请求的多个goroutine之间的请求域的数据、超时、退出等操作。context可以代替cannel和waitgroup组合。
*/
// 错误和异常
/*
为了增加程序的健壮性,防止外部引用的包异常导致我们的程序异常。使用recover统一获取panic
errors.Wrap和errors.WrapWithCode是对error的二次包装
errors.ToJson/FromJson可以通过json网络传播error
error是接口,给该接口返回空指针,外部接收到的接口不是nil,而是个空 指针的接口。
错误:func returnerr()error{
var p *MyError=nil
if bad(){
p=ErrBad
}
return p //此时就算p是nil,外部拿到的返回值也不是nil,而是一个有值的接口,接口值是空指针
}
改正:func returnerr()error{
if bad(){
return (*MyError)(err)
}
return nil
}
recover必须在panic中才能捕获异常。
recover被二次包装在panic中调用也是无效的。defer func(){func(){recover()}()}()
两个defer嵌套,内部的defer中调用recover也是无效的。 defer func(){defer func(recover()){}()}()
defer直接调用recover也是无效的。 defer recover()
panic(nil)会导致无法捕获异常
有效的调用:
defer func(recover()){}()
panic(1)
*/
Golang常见问题
猜你喜欢
转载自blog.csdn.net/Edu_enth/article/details/103858380
今日推荐
周排行