go基础并发安全和锁

有时候在Go代码中可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发生静态文体(数据静态)。类比现实生活中的例子,有十字路口被个个方向的车竞争;有火车上的卫生间被车厢上的人竞争
例子:

package main

import (
	"fmt"
	"sync"
)

var wy sync.WaitGroup

var x int

func add() {
    
    
	for i := 0; i < 50000; i++ {
    
    
		x += 1
	}
	wy.Done()
}

func main() {
    
    
	wy.Add(2)
	go add()
	go add()
	wy.Wait()
	fmt.Println(x)
}

互斥锁
互斥锁是一种常用的控制共享资源访问的方法
它能够保证同时只有一个goroutine可以访问共享资源。Go语言中使用sync包的Mutex类型来实现互斥锁。 使用互斥锁来修复上面代码的问题:

package main

import (
	"fmt"
	"sync"
)

var wy sync.WaitGroup
var lock sync.Mutex

var x int

func add() {
    
    
	for i := 0; i < 50000; i++ {
    
    
		//加锁
		lock.Lock()
		x += 1
		//解锁
		lock.Unlock()
	}

	wy.Done()
}

func main() {
    
    
	wy.Add(2)
	go add()
	go add()
	wy.Wait()
	fmt.Println(x)
}

使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待锁;当互斥锁释放后,等待的goroutine才可以获取锁进入临界区,多个goroutine同时等待一个锁时,唤醒的策略是随机的。
读写互斥锁
互斥锁是完全互斥的,但是有很多实际的场景下是读多写少的,当我们并发的去读取一个资源不涉及资源修改的时候是没有必要加锁的,这种场景下使用读写锁是更好的一种选择。读写锁在Go语言中使用sync包中的RWMutex类型

读写锁分为两种:读锁和写锁。当一个goroutine获取读锁之后,其他的goroutine如果是获取读锁会继续获得锁,如果是获取写锁就会等待;当一个goroutine获取写锁之后,其他的goroutine无论是获取读锁还是写锁都会等待。

package main

import (
	"fmt"
	"sync"
)

var wy sync.WaitGroup
var lock sync.Mutex
var rwlock sync.RWMutex

var x int

func add() {
    
    
	for i := 0; i < 10; i++ {
    
    
		//加写锁
		rwlock.Lock()
		x += 1
		//解写锁
		rwlock.Unlock()
	}
	wy.Done()
}

func read() {
    
    
	for i := 0; i < 100; i++ {
    
    
		//加读锁
		rwlock.RLock()
		fmt.Println(x)
		//解读锁
		rwlock.RUnlock()
	}
	wy.Done()
}

func main() {
    
    
	wy.Add(3)
	go read()
	go read()
	go add()
	wy.Wait()
}

猜你喜欢

转载自blog.csdn.net/weixin_44865158/article/details/115013566
今日推荐