Golang并发编程|实践记录以及工具使用

121 阅读3分钟

Golang的设计理念包含了高并发场景,语言内置了对协程的支持。协程与线程相比更为轻量,协程工作在线程中,其调度调度由用户态管理,相比线程上下文切换需要陷入内核态,协程的上下文切换在用户态完成更为轻量,是Golang在云原生时代受欢迎的重要原因。

Gorountine

在Golang语言中,协程的具体实现为Goroutine;如下所示我们使用go就可以开启一个协程,golang以main为程序入口,main程序会自动开启一个主协程,我们可以在协程中创建多个协程,但是当主协程退出时子协程也会结束,因此使用goroutine时需要注意子协程是否已经完成。

go func(){
    //执行内容
}()

func a(){
}
go a()

channel

Golang这门语言的一个重要设计理念就是不提倡以共享内存来进行通信倡导使用管道通信,对于协程间的通信Golang语言提供了channel即管道进行通信,chan的创建方式如下。

var buf chan int = make(chan int, 5)
bufB := make(chan int, 5)

chan的典型用法就是一个协程往其中写消息,另一个协程从chan中读数据;chan使用完毕后我们需要调用close关闭chan,但是关闭后chan仍然能接收数据,甚至chan为空仍能从chan中读出数据,因此我们从chan中读取数据的时候如下所示,当chan已被关闭且chan中已无数据时,OK为false用于告知接收方。

buf := make(chan int, 5)
go func(){
    val, ok := <-buf
}()

channel分为有缓存channel和无缓冲channel,两种channel的创建方式如下,两者的区别是写入chan的阻塞机制不同;无缓冲chan当写入goroutine写入后接收goroutine未接收,写入goroutine一直会阻塞等待;有缓存chan创建时会指定缓冲大小,写入goroutine可以一直向chan写入,不需要阻塞等待chan中数据被取走,但是当chan已满时,写入方goroutine仍需要阻塞等待。

bufA := make(chan int) //创建无缓冲管道
bufB := make(chan int, 5) //创建容量为5的有缓冲通道

async.WaitGroup

Golang语言提供了sync包用于支持并发操作。sync.WaitGroup为sync提供的一种goroutine等待机制,其内部维护了一个计数器,如下所示,在主协程中调用Add方法,每个协程中调用Done方法可以让计数器减一,主协程调用Wait方法会阻塞协程一直到其内部计数器为0。

wg := async.WaitGroup{}
wg.Add(1) //设置wg的计数器为1
go func(){
    defer wg.Done()//wg的计数器减1
}()
wg.Wait()//阻塞等待wg的计数器到0

async/atomic

与其他高级编程语言一样,Golang语言层面是也支持底层CPU的原子操作,原子操作可以保证在并发操作中对于一个原子操作的原子性,原子性为一个操作为不可分割的整体即要么全部执行要么全部不执行。如下所示,async/atomic包提供了原子加、CAS等操作。

var sum int32 = 0
atomic.AddInt32(&sum, 1)//原子性加法
atomic.CompareAndSwapInt32(&sum, 1, 2)//CAS操作