1-如何解决资源访问的并发问题

1、Mutex

互斥锁 Mutex、读写锁 RWMutex、并发编排WaitGroup

条件变CondChannel 等同步原语。所以,在这里

我先和你说一下同步原语的适用场景。

  1. 共享资源。并发地读写共享资源,会出现数据竞争(data race)的问题,所以需要Mutex、RWMutex 这样的并发原语来保护。

  2. 任务编排。需要 goroutine 按照一定的规律执行,而 goroutine 之间有相互等待或者依赖的顺序关系,我们常常使用 WaitGroup 或者 Channel 来实现。

  3. 消息传递。信息交流以及不同的 goroutine 之间的线程安全的数据交流,常常使用Channel 来实现。

1.1、mutex的基本顺使用方法

Locker 接口

type Locker interface {
Lock()
Unlock()
}

可以看到,Go 定义的锁接口的方法集很简单,就是请求锁(Lock)和释放锁(Unlock)这两个方法
Mutex 以及后面会介绍的读写锁 RWMutex 都实现了 Locker 接口


简单来说,互斥锁 Mutex 就提供两个方法 Lock 和 Unlock:进入临界区之前调用 Lock方法,退出临界区的时候调用 Unlock 方法:

Mutex:如何解决资源并发访问问题?
当一个 goroutine 通过调用 Lock 方法获得了这个锁的拥有权后, 其它请求锁的
goroutine 就会阻塞在 Lock 方法的调用上,直到锁被释放并且自己获取到了这个锁的拥有权。

package main

/*
mutex 的使用
在这个例子中,我们创建了 10 个 goroutine,同时不断地对一个变量(count)进行加 1
操作,每个 goroutine 负责执行 10 万次的加 1 操作,我们期望的最后计数的结果是 10 *
100000 = 1000000 (一百万)。
*/
import (
	"fmt"
	"sync"
)

func main() {
	var count = 0
	// 使用WaitGroup等待10个goroutine完成
	var wg sync.WaitGroup
	wg.Add(10)
	for i := 0; i < 10; i++ {
		go func() {
			defer wg.Done()
			// 对变量count执行10次加1
			for j := 0; j < 100000; j++ {
				count++
			}
		}()
	}
	// 等待10个goroutine完成
	wg.Wait()
	fmt.Println(count)
}

sync.WaitGroup 来等待所有的 goroutine 执行完毕后,再输出
最终的结果。sync.WaitGroup 这个同步原语我会在后面的课程中具体介绍,现在你只需
要知道,我们使用它来控制等待一组 goroutine 全部做完任务。

count ++ 作为一个共享数据,被所有goroutine所共用,上面的代码我们很难发现问题所以 我们可以使用go run -race counter.go

我们可以使用这个 go tool complie -race -S counter.go这个可以直接查看代码前后编译后的结果,可以成功检测出来 data race问题

1.2、mutex的其他用法

切入字段的方式

func main() {
var counter Counter
var wg sync.WaitGroup
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
for j := 0; j < 100000; j++ {
counter.Lock()
counter.Count++
counter.Unlock()
}
}()
}
wg.Wait()
fmt.Println(counter.Count)
}
type Counter struct {
sync.Mutex
Count uint64
}

如果嵌入的 struct 有多个字段,我们一般会把 Mutex 放在要控制的字段上面,然后使用

​ 你还可以把获取锁、释放锁、计数加一的逻辑封装成一个方法,对外不需要暴露锁
等逻辑:

func main() {
// 封装好的计数器
var counter Counter
var wg sync.WaitGroup
wg.Add(10)
// 启动10个goroutine
for i := 0; i < 10; i++ {
go func() {
defer wg.Done()
// 执行10万次累加
for j := 0; j < 100000; j++ {
counter.Incr() // 受到锁保护的方法
}
}()
}
wg.Wait()
fmt.Println(counter.Count())
}
// 线程安全的计数器类型
type Counter struct {
CounterType int
Name
string
mu sync.Mutex
count uint64
}
// 加1的方法,内部使用互斥锁保护
func (c *Counter) Incr() {
c.mu.Lock()
c.count++
c.mu.Unlock()
}
// 得到计数器的值,也需要锁保护
func (c *Counter) Count() uint64 {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陌微阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值