文章目录
Golang 并发&同步的详细原理和使用技巧
Golang 并发概要说明
并发模型
Golang 的并发模型属于一种很典型的 CSP(communicating sequential processes) 并发模型,其核心是不要通过共享内存来通信,而应该通过通信来共享内存。具体实现,就是通过 goroutine 来实现并发,然后并发的 goroutine 之间通过 Channel 来进行通信; 为此,Golang 的并发也有两个明显特点:
- Golang 非常善于并发,可以很简单的来实现(goroutine),直接一个 go 语句基就搞定,通过提高并发可以显著提高处理效率。
- Golang 的 goroutine 并发协程之间一般是采用通信的方式来共享变量或者逻辑交互,而这其中就可以结合 channel 来让并发协程之间进行共享数据,而 channel 就是各个 goroutine 之间通信的管道。
go 关键字实现并发
Golang 并发是通过 goroutine 协程来实现的,通过 go 关键字可以非常简单的启动一个协程;通过 go 关键字启动协程之后, 主进程并不会等待协程的执行,而是继续执行直至结束。因此,如果父子进程之间要有控制关系的话,就需要同步机制来保证。
Golang 并发&同步的设计和实现
为何需要同步控制
Go 既然天然支持并发,并且可以很简单的实现并发编程,那么这些并发的协程之间,如果同时访问访问内存中的同一个数据,在没有同步的机制下,那么同一个数据的访问一定会出现错乱,因此,在并发的场景,一定要通过同步机制才能确保同一内存数据的正确访问。
并发和 context
当需要进行多批次的计算任务同步,或者需要一对多的协作流程的时候;通过 Context 的关联关系(go 的 context 被设计为包含了父子关系),我们就可以控制子协程的生命周期,而其他的同步方式是无法控制其生命周期的,只能是被动阻塞等待完成或者结束。context 控制子协程的生命周期,是通过 context 的 context.WithTimeout 机制来实现的,这个是一般系统中或者底层各种框架、库的普适用法。
context 对并发做一些控制包括 Context Done 取消、截止时间取消 context.WithDeadline、超时取消 context.WithTimeout 等。
一个简单的 context.WithTimeout 示例如下:
package main
import (
"fmt"
"sync"
"time"
"golang.org/x/net/context"
)
var (
wg sync.WaitGroup
)
func work(ctx context.Context) error {
defer wg.Done()
for i := 0; i < 1000; i++ {
select {
case <-time.After(2 * time.Second):
fmt.Println("Do work ", i)
// we received the signal of cancelation in this channel
case <-ctx.Done():
fmt.Println("Cancel th