go-安全读写共享变量几种方式

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