Go 并发编程机制
学习-极客时间-Go语言从入门到实战 笔记
并发是Golang的核心和优势,掌握它能提高编程效率。有三种基本机制来实现:协程机制,共享内存并发机制和CSP机制。
协程机制
GO 的协程(Goroutine)和系统线程(Thread M)之间有go特有的调度器(Processor),实现协程的挂起和调度,调度也可以将自己切换到别的线程。下面这张图比较简洁:
代码片段举例:
func TestGoroutine(t *testing.T) {
for i := 0;i < 10 ;i++ {
go func(i int) {
fmt.Println(i)
}(i) //要将参数复制到协程,才能运行正确
}
}
共享内存并发机制
协程间共用变量的时候要特别注意,我们可以使用锁的方式和waitGroup来解决。
代码片段举例:
package share_mem
import (
"sync"
"testing"
"time"
)
//用一个计数器举例 这样编写没有办法得到正确结果,
//因为count变量在并发时没有按期望递增
func TestShareMem(t *testing.T) {
count := 0
for i:=0;i <5000 ;i++ {
go func() {
count++
}()
}
time.Sleep(1 * time.Second)
t.Log(count)
}
//加上锁之后 可以保证共享变量的正确传递
func TestShareMemSafe(t *testing.T) {
var mut sync.Mutex
count :=0
for i := 0; i < 5000; i++ {
go func() {
defer func() {
mut.Unlock()
}()
mut.Lock()
count++
}()
}
//但是没有给到充足的运行时间,程序还是可能会提前结束,导致计算错误
time.Sleep(1 * time.Second)
t.Log(count)
}
//更好的办法是使用 waitGroup
func TestShareMemWaitGroup(t *testing.T) {
var w sync.WaitGroup
var mut sync.Mutex
count := 0
for i := 0; i < 5000; i++ {
w.Add(1)
go func() {
defer func() {
mut.Unlock()
}()
mut.Lock()
count++
w.Done()
}()
}
w.Wait()
t.Log(count)
}
CSP并发机制
可以在协程中使用通道(channel)传递结果,达成异步运行程序。通道可指定容量,这样不会阻塞协程,消息发送和接收解耦。
没有指定容量,通道会阻塞:
指定容量,发送与接收解耦:
代码片段举例:
package csp
import (
"fmt"
"testing"
"time"
)
//普通任务
func Service() string {
time.Sleep(time.Millisecond * 50)
return "task is done"
}
//其他任务
func otherTask() {
fmt.Println("Other task start")
time.Sleep(time.Millisecond * 100)
fmt.Println("Other task end")
}
//异步任务
func AsyncService() chan string {
retCh := make(chan string)
//retCh := make(chan string, 1) 指定Chanel容量后,可加快效率,无需等待Chanel被拿走后才能执行其他任务
go func() {
res := Service()
fmt.Println("return result")
retCh <- res
//没有指定容量,goroutine 会被阻塞
fmt.Println("service exit...")
}()
return retCh
}
//测试同步任务
func TestSyncTask(t *testing.T) {
fmt.Println(Service())
otherTask()
// 耗时 0.15s
}
//测试异步任务
func TestAsyncTask(t *testing.T) {
retCh := AsyncService()
otherTask()
fmt.Println(<-retCh)
// 耗时 0.10s
}