这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
go语言并发编程
-
什么是并发编程?
并发编程可以使程序不是顺序执行的,而是可以多个分支同时执行,在go中被称为goroutine。 -
创建goroutine:
在golang中创建一个goroutine非常简单,仅需要一个go关键字,就可创建一个新的goroutine,每个goroutine都对应函数,可以创建多个goroutine去并发的执行函数。 -
runtime包:
runtime.Gosched():退出当前gorountine,让其他gorountine先执行,最后再执行被退出的gorountine。 runtime.Goexit():退出当前gorountine,以后也不执行。 -
channel:
channel可以进行gorountine与gorountine间的通信,内部结构类似于队列遵循先进先出的原则。 -
并发安全和锁:
有时候在Go代码中可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发生竞态问题(数据竞态)。例如两个goroutine去累加变量x的值,这两个goroutine在访问和修改x变量的时候就会存在数据竞争,导致最后的结果与期待的不符。在这种情况下需要加锁以保证并发安全。- 互斥锁示例:
package main import ( "fmt" "sync" ) var x int64 var wg sync.WaitGroup var lock sync.Mutex func add() { for i := 0; i < 5000; i++ { lock.Lock() // 加锁 x ++ lock.Unlock() // 解锁 } wg.Done() } // 结果与预期一致 func main() { wg.Add(2) go add() go add() wg.Wait() fmt.Println(x) }
- 互斥锁示例:
-
Sync:
为了使协程都能完成工作,前面我们是使用time.Sleep()来生硬的控制执行时间,但这在实际应用中一般是不可取的,为此Go语言提供了 sync.WaitGroup 来实现并发任务的同步。 sync.WaitGroup 内部维护着一个计数器,计数器的值可以增加和减少。例如当我们启动了N 个并发任务时,就将计数器值增加N。每个任务完成时通过调用Done()方法将计数器减1。通过调用Wait()来等待并发任务执行完,当计数器值为0时,表示所有并发任务已经完成。 简单使用如下:另:Go语言中内置的map不是并发安全的,Sync包中提供了sync.Map以确保map并发安全。var wg sync.WaitGroup func hello() { defer wg.Done() fmt.Println("Hello Goroutine!") } func main() { wg.Add(1) go hello() // 启动另外一个goroutine去执行hello函数 fmt.Println("main goroutine done!") wg.Wait() }