各位大佬大家好,还是小弟我,这次开发遇到了一个挺坑的问题
首先说一下需求,我要完成一个任务的开始停止,对,就是这么简单。
小弟首先定义了一个全局的 map 变量去存储 job 的信息
//SyncJob 任务详情
type SyncJob struct {
ID string
Ctx context.Context
Cancel context.CancelFunc
}
//Job 单个同步任务
var Job = make(map[string]interface{})
//JobWork job 列表
var JobWork = make(map[string][]map[string]interface{})
然后我定义了一个 startwork 和 stopwork,
//SyncStart 任务开始
func SyncStart(id string) (msg string, err error) {
var job SyncJob
ctx, cancel := context.WithCancel(context.Background())
go do work(id)
job.ID = id
job.Ctx = ctx
job.Cancel = cancel
Job[id] = job
JobWork["job"] = append(JobWork["job"], Job)
return "Start work success", nil
}
//SyncStop 任务结束
func SyncStop(id string) (msg string, err error) {
for _, i := range JobWork["job"] {
jobss := i[id]
op, _ := jobss.(SyncJob)
defer op.Cancel()
}
return "Stop work success", nil
}
我尝试了一下这种写法
infiapi.SyncStart("123456")
time.Sleep(time.Second * 10)
infiapi.SyncStop("123456")
这样是可以停止任务的。 但是!小弟写了一个 web server, 想在 web server 中停止它
func StartSyncwork(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析参数,默认是不会解析的
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
id := r.Form["uid"]
infiapi.SyncStart(string(id[0]))
}
func StopSyncWork(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析参数,默认是不会解析的
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
id := r.Form["uid"]
infiapi.SyncStop(string(id[0]))
}
........
http.HandleFunc("/start", StartSyncwork) //设置访问的路由
http.HandleFunc("/stop", StopSyncWork)
这样调用开启可以,但是 stop 无法停止,是 map 作用域的问题么?这样该如何解决呢??拜谢!
1
OakScript 2020-04-13 19:31:14 +08:00 2
net/http 本质是多 goroutine 模型,golang 的 map 并发读写会 panic...
|
3
songjiaxin2008 2020-04-13 19:40:04 +08:00 1
首先记得加个锁... 不然会有竞争读写问题
可以用`sync.Map` 和作用域无关。 `SyncStop`为什么要用`defer`呢 直接`op.Cancel()` 就可以了 另外你这个多层套娃看的有点晕啊。。。搞个 repo 可以帮你看下 |
4
bobuick 2020-04-13 19:46:01 +08:00
很久没写 golang 了,defer 这样在 for 里面用,疑似有坑。
|
5
songjiaxin2008 2020-04-13 19:47:11 +08:00
for 里面调 defer 有泄漏的可能
|
6
OakScript 2020-04-13 20:07:53 +08:00 1
搞个临时 repo 或者搞个 golang playground 出来吧,这样大家好帮你调试
|
8
PEIENYKYK OP @songjiaxin2008 问题已经解决,加了锁,优化了一下代码,感恩感恩!谢谢您!
|
9
beidounanxizi 2020-04-13 21:08:32 +08:00
把 map 作为一个 channel 穿来穿去 可解忧
|
10
useben 2020-04-13 21:52:50 +08:00 1
1 、for 里面不要 defer, 会有泄露问题
2 、任务调度器一般不这样做的。起一个调度 goroutine 作为调度器,任务队列 chan,每个任务 go 一个协程来执行,并且创建一个 stopChan 来等待 stop 事件停止任务。然后通过 chan 或者队列的方式来投递任务和监听 chan 来消费任务。接口 start 创建任务和扔进 chan 。stop 接口发送 stop 事件到对应 stopChan,达到停止任务的目的 |
12
fighterlyt 2020-04-14 14:06:20 +08:00
@wnanbei 一看就是不知道为什么会泄露
|
13
wnanbei 2020-04-16 16:27:20 +08:00
@fighterlyt 是不知道,然后呢
|
14
fighterlyt 2020-04-16 21:17:29 +08:00
@wnanbei 不用然后,既然你不想知道,那我也没有必要告知,简单提醒一句,for 中 defer 容易泄露,并不是一定会泄露,所以是建议,而不是直接从语法上禁止
|