golang 并发-共享资源安全


首先,我们需要知道:在golang中,多 goroutine 同时操作一个共享资源时;我们需要保障资源的安全(我们对资源的操作结果要符合我们的预期);
当我们未对资源做保护操作时,多个goroutine同时操作同一资源时,就可能会出现问题;

例如 count=1 ,有A、B两个 goroutine 同时拿到资源 count 变量,再对 count 进行更新操作,
然后再打印count;
A 拿到 count 变量(资源)时,count 等于1,
B 拿到 count 变量(资源)时,count 也等于1,
A 对于变量操作一次(进行一次 +1 ),这是 count 就等于2了,count的值被修改成2 ;
B 对于变量操作两次(进行两次 +1 ),这是 count 就等于3了,count的值被修改成3 ;
若 A 先执行完,则 B: count 的值被修改成3;为 count 的最后一次更新,count打印 就为 3;
若 B 先执行完,则 A: count 的值被修改成2;为 count 的最后一次更新,count打印 就为 2;

如若想资源被操作、更新后的结果符合我们的预期,我们就需要使用到 锁, 在 golang sync 包中,可以找到相关使用介绍,较为常用的锁相关有 sync.Map(同步Map,并发安全),sync.Mutex(互斥锁),sync.RWMutex(读写锁)。
举个例子,上代码:

未使用并发版本

package main

import "fmt"

var count = 0

func main() {
	for i := 0; i < 1000; i++ {
		addOne()
	}
	fmt.Println("count:", count)
}
func addOne() {
	count += 1
}

/* 输出结果
count: 1000
*/

多次运行,输出结果

图片.png

使用并发版本, 但未使用锁

package main

import (
	"fmt"
	"sync"
)

var count = 0

func main() {
	wg := sync.WaitGroup{}
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			addOne()
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("count:", count)
}
func addOne() {
	count += 1
}

运行多次,都不符合预期-

注意:如果符合预期1000,可能时电脑性能导致的<我设置成100时,与未多并发程序结果一致>,可加大测试for循环次数,多次验证
图片.png

多并发,且加锁

package main

import (
	"fmt"
	"sync"
)

var count = 0
var mutex = sync.Mutex{}

func main() {

	wg := sync.WaitGroup{}
	for i := 0; i < 10000; i++ {
		wg.Add(1)
		go func() {
			addOne()
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("count:", count)
}
func addOne() {
	mutex.Lock()
	defer mutex.Unlock()
	count += 1
}

运行多次 结果一致

** 注意:** 这里我故意将循环次数调整至 100000 ,以避免一定的偶然性
图片.png

代码优化

常见写法

package main

import (
	"fmt"
	"sync"
)
// 将计数、互斥锁(count、sync.Mutex)放入结构体中 
type Count struct {
	count int
	sync.Mutex
}

var count Count

func main() {

	wg := sync.WaitGroup{}
	for i := 0; i < 100000; i++ {
		wg.Add(1)
		go func() {
			addOne()
			wg.Done()
		}()
	}
	wg.Wait()
	fmt.Println("count:", count.count)
}
func addOne() {
	count.Lock()
	defer count.Unlock()
	count.count += 1
}

总结:
多并发程序,需要多注意临界区(共享)资源的更新问题;
未编写单元测试;总觉得该测试不够正规的;哈哈~