goroutine
1 | go fun(); //在函数前面加个go,即可开启一个goroutine |
- 程序启动时,只有一个goroutine来调用main函数,称之为主goroutine
- 除返回或者进程结束外,go 没有提供其他方式结束goroutine
通道
表达式
chan [type]
,使用map构建,可以设置缓存容量(默认为0),零值为nil
1 | ch1 := make(chan int,3) |
发送
若缓存满了,且无人接收,则阻塞
1 | ch <- x |
接收
若无数据,则阻塞等待
1 | x = <-ch |
关闭
关闭后发送会宕机,关闭后接收会获取所有值
1 | close(ch) |
select多路复用
多个等待条件满足时,选一个执行,被选中的几率相同,若均不满足且无default,则阻塞,
若有default 则为非阻塞处理,执行default中的语句
若通道为则nil,则永远不会被选中,则相关语句被禁用
1 | select { |
广播机制
不在通道上发送值,而是直接关闭它
竞态与互斥
并发:无法确定一个事件肯定先于另一个事件
互斥锁
1 | import "sync" //所在包 |
读写锁
1 | import "sync" //所在包 |
内存同步
- 编译器优化有可能导致指令乱序,若无在通道通信或者互斥操作这类同步原语,两条局部无关,全局有关的相邻的代码可能不被认为无关联关系,从而被优化,出现乱序现象
- cache冲突:若无在通道通信或者互斥操作这类同步原语,CPU将不会cache中的数据刷回内存,从而导致访问变量时出现数据一致性问题
- 全局变量需要用互斥,但解决同步互斥需要成本,所以尽可能把变量放在同一个goroutine中
单例
1 | import "sync" //所在包 |
竞态检测器
在go build
、go run
、go test
中加入参数-race
,将记录在执行时对共享变量的所有访问
线程与goroutine
- 栈空间大小:线程固定(2MB),goroutine动态(2KB-1GB)
- 调度
- 线程是全系统,goroutine是单个进程内部的
- 线程有时间片,goroutine没有,但goroutine在休眠(互斥等待,time.Sleep)时,也会进行切换
- GOMAXPROCS:go程序的线程数,默认为CPU数量,利用系统原语或者被嵌入的其他语言开辟的线程不计入其中。
- goroutine没有句柄,go一直追求一种极简的风格,让任务简单化,避免通过句柄实现任务的全局控制,导致程序复杂化