在 函数 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 以及 对整个更新器的执行效率的优化,