理论基础:Communication Sequential Process(CSP)
不要通过共享内存来通信;通过通信来共享内存
goroutine
- 任何函数只需加上go就能送给调度器运行
- 不需要在定义时区分是否是异步函数
- 调度器在合适的点进行切换
- 使用-race来检测数据访问冲突
- 可能切换的点
- I/O
- select
- 函数调用
- 等待锁
- channel
- runtime.Gosched()
func main(){
for i :=0;i<1000;i++{
go func(test int){
for{
a[test] ++
runtime.Gosched()
}(i)
}
// 因为是并发执行,所以醒一下,否则循环完了,还没来得及输出,输出为空
time.Sleep(time.Millisecond)
}
协程 Coroutine
- 轻量级“线程”
- 非抢占式多任务处理,由协程主动交出控制权
- 编译器/解释器/虚拟机层面的多任务
- 多个协程可能在一个或多个线程上运行
- 子程序是协程的一个特例
- 其他语言
- C++:Boost.Coroutine
- Java:不支持
- Python
- 使用yield关键字实现协程
- Python 3.5加入了async def 对协程原生支持
Channel
一个channel是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息。每个channel都有一个特殊的类型,也就是channels可发送数据的类型。一个可以发送int类型数据的channel一般写为chan int。
package main
import (
"fmt"
"time"
)
func worker(id int, c chan int) {
// 接收方除了使用range来判断close,也可以通过如下代码来判断
// n,ok := <-c
// if !ok {
// break;
// }
for n := range c {
fmt.Printf("Worker %d received %c\n",
id, n)
}
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)
return c
}
func chanDemo() {
// 发数据 chan<- 收数据 <-chan
var channels [10]chan<- int
for i := 0; i < 10; i++ {
channels[i] = createWorker(i)
}
for i := 0; i < 10; i++ {
channels[i] <- 'a' + i
}
for i := 0; i < 10; i++ {
channels[i] <- 'A' + i
}
time.Sleep(time.Millisecond)
}
func bufferedChannel() {
// 添加三个缓冲区,不需要切换协程
c := make(chan int, 3)
go worker(0, c)
c <- 'a'
c <- 'b'
c <- 'c'
c <- 'd'
time.Sleep(time.Millisecond)
}
func channelClose() {
c := make(chan int)
go worker(0, c)
c <- 'a'
c <- 'b'
c <- 'c'
c <- 'd'
// 发送发close
close(c)
time.Sleep(time.Millisecond)
}
func main() {
fmt.Println("Channel as first-class citizen")
chanDemo()
fmt.Println("Buffered channel")
bufferedChannel()
fmt.Println("Channel close and range")
channelClose()
}
传统的同步机制
- WaitGroup
- Mutex
- Cond
测试
传统测试
- 测试数据和测试逻辑混在一起
- 出错信息不明确
- 一旦一个数据出错测试全部结束
@Test public void testAdd(){
assertEquals(3,add(1,2));
assertEquals(2,add(0,2));
assertEquals(0,add(0,0));
assertEquals(0,add(-1,1));
assertEquals(Integer.MIN_VALUE,add(1,Integer.MAX_VALUE));
表格驱动测试
- 分离的测试数据和测试逻辑
- 明确的出错信息
- 可以部分失败
- go语言的语法使得我们更易实践表格驱动测试
测试
- go tool cover -html=c.out
- benchmark
- go tool pprof cpu.out (web graphviz)
- 使用go test获取代码覆盖报告
- 使用go tool cover查看代码覆盖报告
- godoc -http :6060 查看文档
常用标准库
- http
- 使用http客户端发送请求
- 使用http.Client控制请求头部等
- 使用httputil简化工作
- import _ “net/http/pprof”
- 访问/debug/pprof/
- 使用go tool pprof分析性能
- bufio
- log
- encoding/json
- regexp
- time
- strings/math/rand
- 文档
- godoc -http :8888
- 标准库中文版