mutex 安全读写共享变量
func sync_1() {
var wg sync.WaitGroup
var mu sync.Mutex
count := 0
for i := 0; i < 10; i++ {
wg.Add(1)
fmt.Printf("主线程:正在创建第 %d 号协程\n", i) // 新增:主线程创建协程时打印
// 把循环变量i作为参数传入闭包,避免捕获同一个i变量
go func(goroutineID int) {
defer wg.Done()
fmt.Printf("【协程%d】开始执行\n", goroutineID)
for j := 0; j < 5; j++ { // 改成5次,减少打印,方便看
mu.Lock()
count++
fmt.Printf("【协程%d】修改count,现在count=%d\n", goroutineID, count)
mu.Unlock()
time.Sleep(100 * time.Microsecond)
}
fmt.Printf("【协程%d】执行完毕\n", goroutineID)
}(i)
}
fmt.Println("主线程:全部协程已经发起,等待所有协程结束...")
wg.Wait()
fmt.Println("主线程:所有协程完成,最终count =", count)
}
// 结果输出
// 主线程:正在创建第 2 号协程
// 主线程:正在创建第 3 号协程
// 主线程:正在创建第 4 号协程
// 主线程:正在创建第 5 号协程
// 【协程0】开始执行
// 【协程0】修改count,现在count=1
// 【协程2】开始执行
// 【协程2】修改count,现在count=2
// 【协程4】开始执行
// 【协程4】修改count,现在count=3
// 【协程5】开始执行
// 【协程5】修改count,现在count=4
// 【协程1】开始执行
// 【协程3】开始执行
// 主线程:正在创建第 6 号协程
// 【协程1】修改count,现在count=5
// 【协程3】修改count,现在count=6
// 主线程:正在创建第 7 号协程
// 主线程:正在创建第 8 号协程
// 【协程6】开始执行
// 【协程6】修改count,现在count=7
// 【协程7】开始执行
// 【协程7】修改count,现在count=8
// 主线程:正在创建第 9 号协程
// 【协程8】开始执行
// 【协程8】修改count,现在count=9
// 主线程:全部协程已经发起,等待所有协程结束...
// 【协程9】开始执行
// 【协程9】修改count,现在count=10
// 【协程9】修改count,现在count=11
// 【协程4】修改count,现在count=12
// 【协程0】修改count,现在count=13
// 【协程8】修改count,现在count=14
// 【协程6】修改count,现在count=15
// 【协程3】修改count,现在count=16
// 【协程5】修改count,现在count=17
// 【协程1】修改count,现在count=18
// 【协程7】修改count,现在count=19
// 【协程2】修改count,现在count=20
// 【协程0】修改count,现在count=21
// 【协程1】修改count,现在count=22
// 【协程5】修改count,现在count=23
// 【协程6】修改count,现在count=24
// 【协程2】修改count,现在count=25
// 【协程3】修改count,现在count=26
// 【协程4】修改count,现在count=27
// 【协程9】修改count,现在count=28
// 【协程7】修改count,现在count=29
// 【协程8】修改count,现在count=30
// 【协程8】修改count,现在count=31
// 【协程1】修改count,现在count=32
// 【协程7】修改count,现在count=33
// 【协程0】修改count,现在count=34
// 【协程6】修改count,现在count=35
// 【协程3】修改count,现在count=36
// 【协程2】修改count,现在count=37
// 【协程9】修改count,现在count=38
// 【协程5】修改count,现在count=39
// 【协程4】修改count,现在count=40
// 【协程7】修改count,现在count=41
// 【协程8】修改count,现在count=42
// 【协程9】修改count,现在count=43
// 【协程0】修改count,现在count=44
// 【协程1】修改count,现在count=45
// 【协程5】修改count,现在count=46
// 【协程6】修改count,现在count=47
// 【协程3】修改count,现在count=48
// 【协程4】修改count,现在count=49
// 【协程2】修改count,现在count=50
// 【协程8】执行完毕
// 【协程7】执行完毕
// 【协程2】执行完毕
// 【协程4】执行完毕
// 【协程3】执行完毕
// 【协程9】执行完毕
// 【协程6】执行完毕
// 【协程1】执行完毕
// 【协程5】执行完毕
// 【协程0】执行完毕
// 主线程:所有协程完成,最终count = 50
channel通道 安全读写共享变量
func sync_2() {
// 无缓冲通道,用来接收每个协程计算好的结果
resultCh := make(chan int)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
fmt.Printf("主线程:正在创建第 %d 号协程\n", i) // 新增:主线程创建协程时打印
go func(goroutineID int) {
defer wg.Done()
fmt.Printf("【协程%d】开始执行\n", goroutineID)
local := 0 // 每个协程自己的本地变量,不共享!
for j := 0; j < 5; j++ {
local++
fmt.Printf("【协程%d】本地local++,local=%d\n", goroutineID, local)
resultCh <- 1 // 通过channel把本地结果发送出去,移交数据所有权
time.Sleep(100 * time.Microsecond) // 主动让出CPU,方便看到协程交替
}
fmt.Printf("【协程%d】计算完成,发送结果 %d 到channel\n", goroutineID, local)
fmt.Printf("【协程%d】发送完毕\n", goroutineID)
}(i)
}
// 单独协程:等所有worker跑完,关闭channel
go func() {
wg.Wait()
close(resultCh)
fmt.Println("===全部worker协程执行完成,关闭channel===")
}()
total := 0
fmt.Println("主线程:开始从channel读取结果,汇总")
for val := range resultCh { // range读channel,channel关闭后自动退出循环
total += val
fmt.Printf("主线程:收到channel数据 %d,当前汇总total=%d\n", val, total)
}
fmt.Println("主线程:全部接收完毕,最终 total =", total)
}
Building .主线程:正在创建第 0 号协程
主线程:正在创建第 1 号协程
主线程:正在创建第 2 号协程
主线程:正在创建第 3 号协程
主线程:正在创建第 4 号协程
主线程:正在创建第 5 号协程
【协程1】开始执行
【协程1】本地local++,local=1
【协程0】开始执行
【协程2】开始执行
【协程2】本地local++,local=1
【协程4】开始执行
【协程4】本地local++,local=1
【协程0】本地local++,local=1
主线程:正在创建第 6 号协程
【协程5】开始执行
【协程5】本地local++,local=1
主线程:正在创建第 7 号协程
【协程3】开始执行
【协程3】本地local++,local=1
【协程7】开始执行
【协程7】本地local++,local=1
主线程:正在创建第 8 号协程
【协程6】开始执行
【协程6】本地local++,local=1
【协程8】开始执行
主线程:正在创建第 9 号协程
【协程8】本地local++,local=1
【协程9】开始执行
【协程9】本地local++,local=1
主线程:开始从channel读取结果,汇总
主线程:收到channel数据 1,当前汇总total=1
主线程:收到channel数据 1,当前汇总total=2
主线程:收到channel数据 1,当前汇总total=3
主线程:收到channel数据 1,当前汇总total=4
主线程:收到channel数据 1,当前汇总total=5
主线程:收到channel数据 1,当前汇总total=6
主线程:收到channel数据 1,当前汇总total=7
主线程:收到channel数据 1,当前汇总total=8
主线程:收到channel数据 1,当前汇总total=9
主线程:收到channel数据 1,当前汇总total=10
【协程8】本地local++,local=2
【协程6】本地local++,local=2
主线程:收到channel数据 1,当前汇总total=11
【协程1】本地local++,local=2
主线程:收到channel数据 1,当前汇总total=12
【协程9】本地local++,local=2
【协程2】本地local++,local=2
主线程:收到channel数据 1,当前汇总total=13
【协程3】本地local++,local=2
【协程5】本地local++,local=2
【协程0】本地local++,local=2
【协程4】本地local++,local=2
【协程7】本地local++,local=2
主线程:收到channel数据 1,当前汇总total=14
主线程:收到channel数据 1,当前汇总total=15
主线程:收到channel数据 1,当前汇总total=16
主线程:收到channel数据 1,当前汇总total=17
主线程:收到channel数据 1,当前汇总total=18
主线程:收到channel数据 1,当前汇总total=19
主线程:收到channel数据 1,当前汇总total=20
【协程9】本地local++,local=3
【协程7】本地local++,local=3
【协程6】本地local++,local=3
主线程:收到channel数据 1,当前汇总total=21
【协程5】本地local++,local=3
【协程2】本地local++,local=3
【协程0】本地local++,local=3
主线程:收到channel数据 1,当前汇总total=22
主线程:收到channel数据 1,当前汇总total=23
主线程:收到channel数据 1,当前汇总total=24
主线程:收到channel数据 1,当前汇总total=25
主线程:收到channel数据 1,当前汇总total=26
【协程3】本地local++,local=3
主线程:收到channel数据 1,当前汇总total=27
【协程4】本地local++,local=3
【协程8】本地local++,local=3
【协程1】本地local++,local=3
主线程:收到channel数据 1,当前汇总total=28
主线程:收到channel数据 1,当前汇总total=29
主线程:收到channel数据 1,当前汇总total=30
【协程7】本地local++,local=4
【协程3】本地local++,local=4
主线程:收到channel数据 1,当前汇总total=31
【协程8】本地local++,local=4
【协程9】本地local++,local=4
【协程4】本地local++,local=4
【协程6】本地local++,local=4
【协程2】本地local++,local=4
【协程5】本地local++,local=4
主线程:收到channel数据 1,当前汇总total=32
主线程:收到channel数据 1,当前汇总total=33
主线程:收到channel数据 1,当前汇总total=34
主线程:收到channel数据 1,当前汇总total=35
主线程:收到channel数据 1,当前汇总total=36
主线程:收到channel数据 1,当前汇总total=37
主线程:收到channel数据 1,当前汇总total=38
【协程1】本地local++,local=4
主线程:收到channel数据 1,当前汇总total=39
【协程0】本地local++,local=4
主线程:收到channel数据 1,当前汇总total=40
【协程3】本地local++,local=5
主线程:收到channel数据 1,当前汇总total=41
【协程5】本地local++,local=5
【协程0】本地local++,local=5
主线程:收到channel数据 1,当前汇总total=42
【协程6】本地local++,local=5
主线程:收到channel数据 1,当前汇总total=43
主线程:收到channel数据 1,当前汇总total=44
【协程9】本地local++,local=5
【协程2】本地local++,local=5
【协程7】本地local++,local=5
【协程4】本地local++,local=5
【协程1】本地local++,local=5
主线程:收到channel数据 1,当前汇总total=45
主线程:收到channel数据 1,当前汇总total=46
主线程:收到channel数据 1,当前汇总total=47
主线程:收到channel数据 1,当前汇总total=48
主线程:收到channel数据 1,当前汇总total=49
【协程8】本地local++,local=5
主线程:收到channel数据 1,当前汇总total=50
【协程6】计算完成,发送结果 5 到channel
【协程6】发送完毕
【协程3】计算完成,发送结果 5 到channel
【协程3】发送完毕
【协程0】计算完成,发送结果 5 到channel
【协程0】发送完毕
【协程1】计算完成,发送结果 5 到channel
【协程1】发送完毕
【协程9】计算完成,发送结果 5 到channel
【协程9】发送完毕
【协程7】计算完成,发送结果 5 到channel
【协程7】发送完毕
【协程2】计算完成,发送结果 5 到channel
【协程8】计算完成,发送结果 5 到channel
【协程8】发送完毕
【协程5】计算完成,发送结果 5 到channel
【协程5】发送完毕
【协程2】发送完毕
【协程4】计算完成,发送结果 5 到channel
【协程4】发送完毕
===全部worker协程执行完成,关闭channel===
主线程:全部接收完毕,最终 total = 50
原子操作(atomic) 安全读写共享变量
// 原子操作(atomic) 安全读写共享变量
func sync_3() {
var wg sync.WaitGroup
var count int64 = 0 // atomic一般用int64
for i := 0; i < 10; i++ {
wg.Add(1)
fmt.Printf("主线程:正在创建第 %d 号协程\n", i) // 新增:主线程创建协程时打印
go func(goroutineID int) {
defer wg.Done()
fmt.Printf("【协程%d】开始执行\n", goroutineID)
for j := 0; j < 5; j++ {
// 原子 +1
newVal := atomic.AddInt64(&count, 1)
fmt.Printf("【协程%d】修改count,现在count=%d\n", goroutineID, newVal)
time.Sleep(100 * time.Microsecond) // 休眠一小会儿,goroutine进入睡眠,到期后变回就绪
// runtime.Gosched() // 主动让出CPU
}
fmt.Printf("【协程%d】执行完毕\n", goroutineID)
}(i)
}
fmt.Println("=====主线程:全部协程已经发起,等待所有协程结束...=====")
wg.Wait()
// 原子读取最新值
final := atomic.LoadInt64(&count)
fmt.Println("主线程:所有协程完成,最终count =", final)
}
Building .主线程:正在创建第 0 号协程
主线程:正在创建第 1 号协程
主线程:正在创建第 2 号协程
【协程1】开始执行
主线程:正在创建第 3 号协程
【协程1】修改count,现在count=1
【协程0】开始执行
【协程0】修改count,现在count=2
【协程3】开始执行
【协程3】修改count,现在count=3
主线程:正在创建第 4 号协程
【协程2】开始执行
【协程2】修改count,现在count=4
【协程4】开始执行
【协程4】修改count,现在count=5
主线程:正在创建第 5 号协程
主线程:正在创建第 6 号协程
主线程:正在创建第 7 号协程
主线程:正在创建第 8 号协程
主线程:正在创建第 9 号协程
【协程5】开始执行
【协程5】修改count,现在count=6
【协程6】开始执行
【协程6】修改count,现在count=7
【协程8】开始执行
【协程8】修改count,现在count=8
【协程9】开始执行
=====主线程:全部协程已经发起,等待所有协程结束...=====
【协程7】开始执行
【协程9】修改count,现在count=9
【协程7】修改count,现在count=10
【协程6】修改count,现在count=11
【协程1】修改count,现在count=12
【协程7】修改count,现在count=13
【协程9】修改count,现在count=14
【协程5】修改count,现在count=15
【协程3】修改count,现在count=16
【协程8】修改count,现在count=17
【协程0】修改count,现在count=19
【协程4】修改count,现在count=18
【协程2】修改count,现在count=20
【协程5】修改count,现在count=21
【协程9】修改count,现在count=22
【协程2】修改count,现在count=23
【协程4】修改count,现在count=24
【协程0】修改count,现在count=25
【协程7】修改count,现在count=28
【协程8】修改count,现在count=29
【协程3】修改count,现在count=30
【协程6】修改count,现在count=27
【协程1】修改count,现在count=26
【协程6】修改count,现在count=31
【协程3】修改count,现在count=32
【协程1】修改count,现在count=33
【协程4】修改count,现在count=35
【协程8】修改count,现在count=37
【协程5】修改count,现在count=38
【协程9】修改count,现在count=36
【协程0】修改count,现在count=39
【协程7】修改count,现在count=34
【协程2】修改count,现在count=40
【协程7】修改count,现在count=41
【协程0】修改count,现在count=42
【协程2】修改count,现在count=43
【协程4】修改count,现在count=44
【协程5】修改count,现在count=45
【协程6】修改count,现在count=47
【协程9】修改count,现在count=46
【协程1】修改count,现在count=48
【协程3】修改count,现在count=49
【协程8】修改count,现在count=50
【协程8】执行完毕
【协程9】执行完毕
【协程6】执行完毕
【协程7】执行完毕
【协程2】执行完毕
【协程1】执行完毕
【协程0】执行完毕
【协程4】执行完毕
【协程3】执行完毕
【协程5】执行完毕
主线程:所有协程完成,最终count = 50