目录
一、并发机制
1、非线程安全
package share_mem
import (
"fmt"
"sync"
"testing"
"time"
)
//线程不安全计数器
func TestCounter(t *testing.T) {
var counter int
for i := 0; i < 5000; i++ {
go func() {
counter++
}()
}
//这里等一秒是因为我们不知道所有的协程执行完毕需要多久,
//防止主程序已经退出了,协程还没执行完导致结果不准确,后面会介绍更好的处理方案。
time.Sleep(time.Second * 1)
fmt.Printf("counter=%d\n", counter)
}
/*
可以看到下面的执行结果,counter 并不等于 5000
=== RUN TestCounter
counter=4775
--- PASS: TestCounter (1.01s)
PASS
*/
当我们不使用锁的机制来使用协程时,counter 变量在不同的协程里面去做自增,导致了并发时锁的竞争,丢失了很多正确的写操作,即不是一个线程安全的程序。
2、线程安全
那么我们现在就要对这个共享内存做一个线程保护
package share_mem
import (
"fmt"
"sync"
"testing"
"time"
)
//线程安全计数器
func TestCounterThreadSafe(t *testing.T) {
var mut sync.Mutex
var counter int
for i := 0; i < 5000; i++ {
go func() {
//锁的释放我们一般都要写在 defer 里面,
//是为了防止某一些未知异常被触发时,
//忘了将锁释放锁而导致程序整体被挂起
defer func() {
mut.Unlock()
}()
//共享内存加锁
mut.Lock()
counter++
}()
}
//这里等一秒是因为我们不知道所有的协程执行完毕需要多久,
//防止主程序已经退出了,协程还没执行完导致结果不准确,后面会介绍更好的处理方案。
time.Sleep(time.Second * 1)
fmt.Printf("counter=%d\n", counter)
}
/*
可以看到下面的执行结果,counter 等于 5000
=== RUN TestCounterThreadSafe
counter=5000
--- PASS: TestCounterThreadSafe (1.01s)
PASS
*/
二、WaitGroup - 同步等待协程执行结果
WaitGroup 的作用就是只有 Wait 到所有协程里面的东西都执行完毕之后,程序才能继续往下执行,
package share_mem
import (
"fmt"
"sync"
"testing"
"time"
)
//WaitGroup 优化版线程安全计数器
func TestCounterWaitGroup(t *testing.T) {
var mut sync.Mutex
var wg sync.WaitGroup
var counter int
for i := 0; i < 5000; i++ {
//每启动一个协程,我们的等待数量就要加一
wg.Add(1 )
go func() {
//锁的释放我们一般都要写在 defer 里面,
//是为了防止某一些未知异常被触发时,
//忘了将锁释放锁而导致程序整体被挂起
defer func() {
mut.Unlock()
}()
//共享内存加锁
mut.Lock()
//每完成一个协程,就执行 Done(),表示已完成
wg.Done()
counter++
}()
}
//一直等,等到所有协程都执行完毕后,程序才往下继续执行
wg.Wait()
fmt.Printf("counter=%d\n", counter)
}
/*
可以看到下面的执行结果,counter 仍然等于 5000
=== RUN TestCounterWaitGroup
counter=5000
--- PASS: TestCounterWaitGroup (0.00s)
PASS
*/
那么 WaitGroup 为什么更好呢,可以看一下最后的执行时间,如果采用 time.Sleep(),因为我们并不知道500个协程要执行多久,这个时间不好把控,我们为了得到正确的结果,人为预估了 1 秒,但是实际上只需要 0.00 秒就能执行完毕,故用 WaitGroup 即能防止错误的预估协程的执行时间,又能保证线程安全,是上上之选。
三、总结
//互斥锁
var mut sync.Mutex
//同步阻塞等待
var wg sync.WaitGroup
//每启动一个协程,我们的等待数量就要加一
wg.Add(1)
go func() {
defer func() {
//释放锁
mut.Unlock()
}()
//共享内存加锁
mut.Lock()
//每完成一个协程,就执行 Done(),表示已完成
wg.Done()
}()
//一直等,等到所有协程都执行完毕后,程序才往下继续执行
wg.Wait()
注:这篇博文是我学习中的总结,如有转载请注明出处:
https://blog.csdn.net/DaiChuanrong/article/details/118269870
上一篇:Go-协程机制
下一篇:Go-CSP并发机制