您的位置 首页 golang

golang基于共享变量的并发

文章参考go语言圣经,并进行了整理,希望可以帮到工作中有需要的小伙伴~

在一个线性(只有一个goroutine)程序中,程序的执行顺序由程序的逻辑来决定。在有两个或者更多goroutine的程序中,每一个goroutine内的语句也是按照顺序去执行的,但是没法知道不同goroutine中事件的执行顺序,当无法确认一个事件是在另一个事件前面或后面发生的时候,说明这些事件是并发的

1、sync.Mutex互斥锁

在并发模型中,可以用互斥锁来处理并发问题,锁本身可以认为是一个共享变量,一个线程加了互斥锁后,其他线程只能等待,直到锁被释放才能获取新锁。在go语言中sync包里的Mutex类型直接支持互斥锁,Lock方法可以获取锁,Unlock方法会释放锁

 // 示例
var (
	balance int
	mu      sync.Mutex
	wg      sync.WaitGroup
)
func Deposit(amount int) {
	mu.Lock() // 加锁
	balance = balance + amount
	fmt.Println("存入金额:" + strconv.Itoa(amount))
	mu.Unlock() // 释放锁
	// 在goroutine完成任务后,调用wg.Done(),等同于wg.Add(-1)
	wg.Done()
}
func Balance() int {
	mu.Lock()
	// 释放资源
	defer mu.Unlock()
	return balance
}
func main() {
	wg.Add(3)
	// 启动3个goroutine
	go Deposit(100)
	go Deposit(200)
	go Deposit(300)
	// 阻塞主线程,等待所有goroutine完成调用
	// 当所有goroutine都调用完wg.Done()之后才返回
	wg.Wait()
	fmt.Println(Balance())
}
// 输出
存入金额:300
存入金额:100
存入金额:200
账户总额:60总结1> Mutex 为互斥锁,Lock() 加锁,Unlock() 解锁2> 在一个 goroutine 获得 Mutex 后,其它 goroutine 只能等待这个 goroutine 释放该 Mutex3> 使用 Lock() 加锁后,不能再继续对其加锁,通过 Unlock() 解锁后才能再次加锁4> 在 Lock() 之前使用 Unlock() 会导致 panic 异常5> 在同一个 goroutine 中的 Mutex 解锁之前再次进行加锁,会导致死锁6> 常用于读写不确定,并且只有一个读或者写的场景  
 // 示例
func main() {
    var mutex sync.Mutex // 互斥锁
	mutex.Lock()
	fmt.Println("Locked")
	c := make([]chan int, 4)
	for i := 0; i < 3; i++ {
		c[i] = make(chan int)
		go func(i int, c chan int) {
			fmt.Println("Start: ", i)
			mutex.Lock()
			fmt.Println("Goroutine Locked: ", i)
			time.Sleep(time.Second)
			fmt.Println("Goroutine Unlock: ", i)
			mutex.Unlock()
			c <- i
		}(i, c[i])
	}
	time.Sleep(time.Second)
	fmt.Println("Unlock")
	mutex.Unlock()
	time.Sleep(time.Second)
	for _, c := range channels {
		<-c
	}
}  

2、sync.RWMutex读写锁

 // 示例
var (
	balance = 10
	//mu      sync.Mutex // 互斥锁
	mu sync.RWMutex // 读写锁
	wg sync.WaitGroup
)
func Deposit() {
	if Balance() > 0 {
		mu.Lock()
		if balance > 0 {
			balance--
			fmt.Println("当前余额:" + strconv.Itoa(balance))
		} else {
			fmt.Println("余额不足...")
		}
		mu.Unlock()
	} else {
		fmt.Println("余额不足.")
	}
	wg.Done()
}
func Balance() int {
	// 注:可分别通过方式1和方式2,观察程序运行的差异~
	
	// 方式1
	// 使用互斥锁(Mutex),读取的时候会阻塞其它goroutine运行
	mu.Lock()
	defer mu.Unlock()
	// 方式2
	// 使用读写锁(RWMutex),多个goroutine可以同时读取
	//mu.RLock()
	//defer mu.RUnlock()
	return balance
}
func main() {
	for i := 0; i < 20; i++ {
		wg.Add(1)
		go Deposit()
	}
	wg.Wait()
}  

1> RWMutex 是单写多读锁,该锁可以加多个读锁或者一个写锁
2> 读锁占用的情况下会阻止写,不会阻止读,多个 goroutine 可以同时获取读锁
3> 写锁会阻止其他 goroutine 读写,整个锁由该 goroutine 独占
4> 多用于读多写少的场景

#Lock() 和 Unlock()
在加写锁之前已经有其它的读锁和写锁,则 Lock() 会阻塞直到该锁可用

 // 读写锁示例1
func main() {
    var mutex sync.RWMutex // 读写锁
	mutex.Lock()
	fmt.Println("Locked")
	c := make([]chan int, 4)
	for i := 0; i < 3; i++ {
		c[i] = make(chan int)
		go func(i int, c chan int) {
			fmt.Println("Start: ", i)
			mutex.Lock() // Lock() 
			fmt.Println("Goroutine Locked: ", i)
			time.Sleep(time.Second)
			fmt.Println("Goroutine Unlock: ", i)
			mutex.Unlock() // Unlock()
			c <- i
		}(i, c[i])
	}
	time.Sleep(time.Second)
	fmt.Println("Unlock")
	mutex.Unlock()
	time.Sleep(time.Second)
	for _, c := range c {
		<-c
	}
}
// 输出
Locked
Start:  3
Start:  2
Start:  1
Start:  0
Unlock
Goroutine Locked:  3
Goroutine Unlock:  3
Goroutine Locked:  2
Goroutine Unlock:  2
Goroutine Locked:  1
Goroutine Unlock:  1
Goroutine Locked:  0
Goroutine Unlock:  0
  

#RLock() 和 RUnlock()
RLock() 加读锁时,如果存在写锁,则无法加读锁;当只有读锁或者没有锁时,可以加读锁,读锁可以加多个

 // 读写锁示例2
func main() {
    var mutex sync.RWMutex // 读写锁
	mutex.Lock()
	fmt.Println("Locked")
	c := make([]chan int, 4)
	for i := 0; i < 3; i++ {
		c[i] = make(chan int)
		go func(i int, c chan int) {
			fmt.Println("Start: ", i)
			mutex.RLock() // RLock()
			fmt.Println("Goroutine Locked: ", i)
			fmt.Println("Goroutine Unlock: ", i)
			mutex.RUnlock() // RUnlock()
			c <- i
		}(i, c[i])
	}
	time.Sleep(time.Second)
	fmt.Println("Unlock")
	mutex.Unlock()
	time.Sleep(time.Second)
	for _, c := range c {
		<-c
	}
}
// 输出
Locked
Start:  0
Start:  2
Start:  3
Start:  1
Unlock
Goroutine Locked:  1
Goroutine Unlock:  1
Goroutine Locked:  2
Goroutine Unlock:  2
Goroutine Locked:  3
Goroutine Locked:  0
Goroutine Unlock:  0
Goroutine Unlock:  3  

#有兴趣的小伙伴可以将读写锁示例1和示例2运行一下,对比打印出来数据的差别,可以有助于理解锁之间的差异~

文章来源:智云一二三科技

文章标题:golang基于共享变量的并发

文章地址:https://www.zhihuclub.com/98238.shtml

关于作者: 智云科技

热门文章

网站地图