Go 协程并发(并行)资源竞争问题及全局互斥锁

协程并发(并行)资源竞争问题

  • 先看需求
    • 需求:现在要计算 1-200 的各个数的阶乘,并且把各个数的阶乘放入到 map 中。最后显示出来。
    • 要求使用 goroutine 完成
  • 分析思路:
    • 1) 使用 goroutine 来完成,效率高,但是会出现并发/并行安全问题. 
    • 2) 这里就提出了不同 goroutine 如何通信的问题
  • 代码实现
    • 1) 使用 goroutine 来完成(看看使用 gorotine 并发完成会出现什么问题? 然后去解决)
    • 2) 在运行某个程序时,如何知道是否存在资源竞争问题在编译该程序时,增加一个参数 -race 即可 
package main
import (
    "fmt"
    "time"
)
// 思路
// 1. 编写一个函数,来计算各个数的阶乘,并放入到 map中.
// 2. 我们启动的协程多个,统计的将结果放入到 map中
// 3. map 应该做出一个全局的.

var (
	myMap = make(map[int]int, 10)  
)

// test 函数就是计算 n!, 让将这个结果放入到 myMap
func test(n int) {
	
	res := 1
	for i := 1; i <= n; i++ {
		res *= i
	}

}

func main() {

	// 这里开启多个协程完成这个任务[200个]
	for i := 1; i <= 20; i++ {
		go test(i)
	}


	//休眠10秒钟【第二个问题 】
	time.Sleep(time.Second * 5)

	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
}
  • 输出结果:

  • 资源竞争问题示意图解析:


  • go build -race main.go

不同 goroutine 之间如何通讯

  • 全局变量的互斥锁
  • 使用管道 channel 来解决

使用全局变量加锁同步改进程序

  • 示意图:

  • 因为没有对全局变量 m 加锁,因此会出现资源争夺问题,代码会出现错误,提示 concurrent mapwrites
  • 解决方案:加入互斥锁
  • 由于数的阶乘很大,结果会越界,可以将求阶乘改成 uint64(i)

package sync

import "sync"
  • sync包提供了基本的同步基元,如互斥锁。
  • 除了Once和WaitGroup类型,大部分都是适用于低水平程序线程,高水平的同步使用channel通信更好一些

type Mutex

type Mutex struct {
    // 包含隐藏或非导出字段
}
  • Mutex是一个互斥锁,可以创建为其他结构体的字段;零值为解锁状态。
  • Mutex类型的锁和线程无关,可以由不同的线程加锁和解锁。

func (*Mutex) Lock

func (m *Mutex) Lock()
  • Lock方法锁住m,如果m已经加锁,则阻塞直到m解锁。

func (*Mutex) Unlock

func (m *Mutex) Unlock()
  • Unlock方法解锁m,如果m未加锁会导致运行时错误。
  • 锁和线程无关,可以由不同的线程加锁和解锁。
package main
import (
	"fmt"
	"time"
	"sync"
)

var (
	myMap = make(map[int]int, 10)  
	//声明一个全局的互斥锁
	//lock 是一个全局的互斥锁, 
	//sync 是包: synchornized 同步
	//Mutex : 是互斥
	lock sync.Mutex
)

// test 函数就是计算 n!, 让将这个结果放入到 myMap
func test(n int) {
	
	res := 1
	for i := 1; i <= n; i++ {
		res *= i
	}

	lock.Lock()
	myMap[n] = res //concurrent map writes?
	//解锁
	lock.Unlock()
}

func main() {

	// 我们这里开启多个协程完成这个任务[200个]
	for i := 1; i <= 20; i++ {
		go test(i)
	}


	//休眠10秒钟【第二个问题 】
	time.Sleep(time.Second * 5)

	//这里我们输出结果,变量这个结果
	lock.Lock()
	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
	lock.Unlock()
}

 


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值