go 实现的是 CSP (Communicating Sequential Process,通讯顺序进程) 模型
CSP
CSP (Communicating Sequential Process,通讯顺序进程) 模型是上个世纪七十年代提出的,不同于传统的多线程通过共享内存来通信,CSP 讲究的是 以通信的方式来共享内存。用于描述两个独立的并发实体通过共享的通讯 channel(管道)进行通信的并发模型。 CSP 中channel
是第一类对象,它不关注发送消息的实体,而关注与发送消息时使用的channel
。
Golang 就是借用 CSP 模型的一些概念为之实现并发进行理论支持,其实从实际上出发,go 语言并没有完全实现了 CSP 模型的所有理论,仅仅是借用了 process
和channel
这两个概念。**process
是在 go 语言上的表现就是 goroutine
是实际并发执行的实体,每个实体之间是通过channel
通讯来实现数据共享**。
chanel
Golang 中channel
是被单独创建并且可以在goroutine
并发实体之间传递,它的通信模式类似于 boss-worker
模式的,一个实体通过将消息发送到channel
中,然后又监听这个 channel
的实体处理,两个实体之间是匿名的,这个就实现实体中间的解耦,其中 channel
是同步的,一个消息被发送到 channel
中,最终是一定要被另外的实体消费掉的,在实现原理上其实类似一个阻塞的消息队列。
goroutine
Goroutine
是 Golang 实际并发执行的实体,它底层是使用协程(coroutine
)实现并发,coroutine
是一种运行在用户态的用户线程,类似于greenthread
,go 底层选择使用coroutine
的出发点是因为,它具有以下特点:
- 用户空间 避免了内核态和用户态的切换导致的成本。
- 可以由语言和框架层进行调度。
- 更小的栈空间允许创建大量的实例。
更多请看 →线程/协程详解
Goroutine 是异步执行的
有的时候为了防止在结束 main 函数的时候结束掉Goroutine
,所以需要同步等待,可以使用一下三种方式:
channel
1
2ch := make(chan int) 无缓冲的channel由于没有缓冲发送和接收需要同步.
ch := make(chan int, 2) 有缓冲channel不要求发送和接收操作同步.例子:
1
2
3
4
5
6
7func main() {
ch := make(chan struct{})
go func() { fmt.Println("start working")
time.Sleep(time.Second * 1)
ch <- struct{}{} }()
<-ch //会阻塞直到协程完成
}sync.WaitGroup
在 sync 包中,提供了 WaitGroup ,它会等待它收集的所有 goroutine 任务全部完成。在 WaitGroup 里主要有三个方法:
Add, 可以添加或减少 goroutine 的数量.
Done, 相当于 Add(-1).
Wait, 执行后会堵塞主线程,直到 WaitGroup 里的值减至 0.例子:
1
2
3
4
5
6
7
8
9
10func main() {
var wg sync.WaitGroup
var urls = []string{"http://www.golang.org/", "http://www.google.com/",}
for _, url := range urls {
wg.Add(1)
go func(url string) { defer wg.Done()
http.Get(url) }(url)
}
wg.Wait()
}context
context 包主要是用来处理多个 goroutine 之间共享数据,及多个 goroutine 的管理。context 包的核心是 struct Context,接口声明如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// A Context carries a deadline, cancelation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
// Done 返回一个只能接受数据的channel类型,当该context关闭或者超时时间到了的时候,
//该channel就会有一个取消信号
Done() <-chan struct{}
// Err 在Done() 之后,返回context 取消的原因。
Err() error
// Deadline 设置该context cancel的时间点
Deadline() (deadline time.Time, ok bool)
// Value 允许 Context 对象携带request作用域的数据,该数据必须是线程安全的。
Value(key interface{}) interface{}
}Context 对象是线程安全的,你可以把一个 Context 对象传递给任意个数的 gorotuine,对它执行 取消 操作时,所有 goroutine 都会接收到取消信号。
面试题
双协程交替打印 1-10
1 | // 双协程交替打印1-10 |