V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
PEIENYKYK
V2EX  ›  Go 编程语言

Golang map 全局变量使用问题,请各位大佬留步一观。

  •  
  •   PEIENYKYK · 2020-04-13 19:27:12 +08:00 · 5128 次点击
    这是一个创建于 1685 天前的主题,其中的信息可能已经有所发展或是发生改变。

    各位大佬大家好,还是小弟我,这次开发遇到了一个挺坑的问题
    首先说一下需求,我要完成一个任务的开始停止,对,就是这么简单。
    小弟首先定义了一个全局的 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 作用域的问题么?这样该如何解决呢??拜谢!

    16 条回复    2020-04-17 11:47:43 +08:00
    OakScript
        1
    OakScript  
       2020-04-13 19:31:14 +08:00   ❤️ 2
    net/http 本质是多 goroutine 模型,golang 的 map 并发读写会 panic...
    PEIENYKYK
        2
    PEIENYKYK  
    OP
       2020-04-13 19:35:54 +08:00
    @OakScript 我试下加锁,感恩老哥
    songjiaxin2008
        3
    songjiaxin2008  
       2020-04-13 19:40:04 +08:00   ❤️ 1
    首先记得加个锁... 不然会有竞争读写问题
    可以用`sync.Map`

    和作用域无关。
    `SyncStop`为什么要用`defer`呢 直接`op.Cancel()` 就可以了
    另外你这个多层套娃看的有点晕啊。。。搞个 repo 可以帮你看下
    bobuick
        4
    bobuick  
       2020-04-13 19:46:01 +08:00
    很久没写 golang 了,defer 这样在 for 里面用,疑似有坑。
    songjiaxin2008
        5
    songjiaxin2008  
       2020-04-13 19:47:11 +08:00
    for 里面调 defer 有泄漏的可能
    OakScript
        6
    OakScript  
       2020-04-13 20:07:53 +08:00   ❤️ 1
    搞个临时 repo 或者搞个 golang playground 出来吧,这样大家好帮你调试
    PEIENYKYK
        7
    PEIENYKYK  
    OP
       2020-04-13 21:07:22 +08:00
    @OakScript 谢谢老哥,问题已经解决,加了锁,优化了一下代码,感恩感恩!谢谢您!
    PEIENYKYK
        8
    PEIENYKYK  
    OP
       2020-04-13 21:07:40 +08:00
    @songjiaxin2008 问题已经解决,加了锁,优化了一下代码,感恩感恩!谢谢您!
    beidounanxizi
        9
    beidounanxizi  
       2020-04-13 21:08:32 +08:00
    把 map 作为一个 channel 穿来穿去 可解忧
    useben
        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,达到停止任务的目的
    wnanbei
        11
    wnanbei  
       2020-04-14 11:15:57 +08:00
    @useben 现在 1.14 里还会有 for 里面 defer 会泄露的问题吗?
    fighterlyt
        12
    fighterlyt  
       2020-04-14 14:06:20 +08:00
    @wnanbei 一看就是不知道为什么会泄露
    wnanbei
        13
    wnanbei  
       2020-04-16 16:27:20 +08:00
    @fighterlyt 是不知道,然后呢
    fighterlyt
        14
    fighterlyt  
       2020-04-16 21:17:29 +08:00
    @wnanbei 不用然后,既然你不想知道,那我也没有必要告知,简单提醒一句,for 中 defer 容易泄露,并不是一定会泄露,所以是建议,而不是直接从语法上禁止
    jack1998
        15
    jack1998  
       2020-04-17 11:16:14 +08:00
    @useben 这个小心心是怎么弄出来的
    useben
        16
    useben  
       2020-04-17 11:47:43 +08:00
    @jack1998 貌似是楼主感谢留言才会有
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2425 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 16:00 · PVG 00:00 · LAX 08:00 · JFK 11:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.