关于在go并发五 中 存在的问题

本文探讨了在Go语言中使用Map进行并发读写时出现的fatal error: concurrent map read and map write问题,通过引入sync.Mutex锁或使用sync.Map来避免资源竞争,提升多goroutine环境下程序的稳定性和执行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在 函数 TestStopTimeUpdate() 中,有这样一句代码,是存放在每一个分类下面的 书籍,然后 key 为书籍的分类名,具体的 元素 []int 为 预设的分页码,这样就达到 每一类 下面 每次都能抓取 新的书籍内容。

mapPage := make(map[string][]int)

但是 在代码中 有 对这个分页map 有读和写 操作

mapPage[tuName] = sliceInt
...
...
mapPage[tuName] = mapPage[tuName][1:len(mapPage[tuName])]

表面上,程序是运行的通,但是在,多个goroutine 当中,并发运行就产生 fatal error:concurrent map read and map write的错误 ,也就是并发 读写错误,
其原因是 map 本身是引用类型 ,在调用传递 参数时,多个goroutine,就会产生资源竞争情况,因此 最为简单暴力的方式 是 加锁 对,就是那个sync.Mutex 互斥锁。

    var sm sync.Mutex

	for ii, vv := range urls {

		tuName := "BookTimerUpdate" + strconv.Itoa(ii)
		mapPage[tuName] = sliceInt
		args := func(args ...interface{}) interface{} {
		   //加入锁
			sm.Lock()
			defer func() {	sm.Unlock()}()
			//----------------------
			//任务完成的定时器
			if len(mapPage[tuName]) == 0 {
				//找到任务完成的调度层名称
				cdName := tuName + "_CD"
				insMap := make(map[string]interface{})
				insMap["Instruction"] = "StopTicker"
				insMap["Data"] = cdName
				fmt.Println("------------------定时器:", tuName, "已经完成抓取-----------")
				return insMap
			} else {

				fmt.Println(mapPage[tuName][0])
				//测试除了定时器6 其他的定时器完成任务之后就全部关闭
				//if tuName!="BookTimerUpdate6" {
				//	mapPage[tuName] = mapPage[tuName][1:len(mapPage[tuName])]
				//}

				mapPage[tuName] = mapPage[tuName][1:len(mapPage[tuName])]
			}

			fmt.Println(vv)
			fmt.Println("一共有多少个goroutine:",runtime.NumGoroutine())
			return nil
		}


接下来 就是加快 定时器的 执行速度。
下面把 cyt := time.Duration(
otheroper.RandInt64(1, 3, true)) * time.Nanosecond
参数 从秒变为 Nanosecond ,然后max 的随机数范围 也调到最小
,然后把 分页页码的max 改为100000,
当然 使用这种方式,的确可以解决问题,但是此时的你可能会直接想到执行效率 问题,没错,你想的是的对,

这里除了这种方式以外,也可以使用sync.Map来替代 map和sync.Mutex这种组合方式

func TestStopTimeUpdate() {

	urls, _ := dao.GetUrlBySite("zongheng")

	ts := []*timerupdate.TimerUpdate{}
	sliceInt := make([]int, 0)
	max := 100
	for i := 0; i < max; i++ {
		sliceInt = append(sliceInt, i+1)
	}
	//mapPage := make(map[string][]int)
	//var sm sync.Mutex
	var smap sync.Map
	for ii, vv := range urls {

		tuName := "BookTimerUpdate" + strconv.Itoa(ii)
		smap.Store(tuName, sliceInt)
		//mapPage[tuName] = sliceInt
		args := func(args ...interface{}) interface{} {
			//sm.Lock()
			//defer func() { sm.Unlock() }()
			//任务完成的定时器
			//if len(mapPage[tuName]) == 0 {
			si, _ := smap.Load(tuName)
		    si2:=si.([]int)
			if len(si2) == 0 {
				//找到任务完成的调度层名称
				cdName := tuName + "_CD"
				insMap := make(map[string]interface{})
				insMap["Instruction"] = "StopTicker"
				insMap["Data"] = cdName
				fmt.Println("------------------定时器:", tuName, "已经完成抓取-----------")
				return insMap
			} else {

				fmt.Println(si2[0])
				//测试除了定时器6 其他的定时器完成任务之后就全部关闭
				//if tuName!="BookTimerUpdate6" {
				//	mapPage[tuName] = mapPage[tuName][1:len(mapPage[tuName])]
				//}
				smap.Store(tuName, si2[1:len(si2)])
				//mapPage[tuName] = mapPage[tuName][1:len(mapPage[tuName])]
			}

			fmt.Println(vv)

			return nil
		}

		cyt := time.Duration(
			otheroper.RandInt64(1, 3, true)) * time.Nanosecond
		tu1 := timerupdate.CreateExcuTu(tuName, 1, cyt, args)
		ts = append(ts, tu1)
	}
	dispatch.GoRun(dispatch.GoWorkerNum(50), ts...)
}

同时也可以摒弃map 这种方式,来传递下标值,从而不使用互斥锁,

好了,感谢你读到这里,下个月,我会继续 加入定时器的功能
1 添加优先级任务处理功能,
2 根据任务的数量,自动来设定worker go 的数量,
3 以及 对整个更新器的执行效率的优化,

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值