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

发布于 1周前 作者 sinazl 来自 Go语言

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

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


更多关于Golang Go语言中 map 全局变量使用问题,请各位大佬留步一观。的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

17 回复

net/http 本质是多 goroutine 模型,golang 的 map 并发读写会 panic…

更多关于Golang Go语言中 map 全局变量使用问题,请各位大佬留步一观。的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我试下加锁,感恩老哥

首先记得加个锁… 不然会有竞争读写问题
可以用sync.Map

和作用域无关。
SyncStop为什么要用defer呢 直接op.Cancel() 就可以了
另外你这个多层套娃看的有点晕啊。。。搞个 repo 可以帮你看下

很久没写 golang 了,defer 这样在 for 里面用,疑似有坑。

for 里面调 defer 有泄漏的可能

搞个临时 repo 或者搞个 golang playground 出来吧,这样大家好帮你调试

谢谢老哥,问题已经解决,加了锁,优化了一下代码,感恩感恩!谢谢您!

问题已经解决,加了锁,优化了一下代码,感恩感恩!谢谢您!

把 map 作为一个 channel 穿来穿去 可解忧

1 、for 里面不要 defer, 会有泄露问题
2 、任务调度器一般不这样做的。起一个调度 goroutine 作为调度器,任务队列 chan,每个任务 go 一个协程来执行,并且创建一个 stopChan 来等待 stop 事件停止任务。然后通过 chan 或者队列的方式来投递任务和监听 chan 来消费任务。接口 start 创建任务和扔进 chan 。stop 接口发送 stop 事件到对应 stopChan,达到停止任务的目的

现在 1.14 里还会有 for 里面 defer 会泄露的问题吗?

一看就是不知道为什么会泄露

是不知道,然后呢

不用然后,既然你不想知道,那我也没有必要告知,简单提醒一句,for 中 defer 容易泄露,并不是一定会泄露,所以是建议,而不是直接从语法上禁止

这个小心心是怎么弄出来的

貌似是楼主感谢留言才会有

在Go语言中,全局变量是一种在包级别声明的变量,它可以在该包的所有文件中访问。当涉及到map类型的全局变量时,有一些关键点需要注意:

  1. 初始化:全局map变量需要在声明时进行初始化,否则它将默认为nil。对nilmap进行操作(如读写)会引发运行时错误。因此,务必在声明时使用make函数进行初始化,例如:

    var myMap = make(map[string]int)
    
  2. 并发访问:全局变量在多个goroutine之间共享时,需要考虑并发访问的问题。对于map,由于其不是并发安全的,所以在多个goroutine同时读写时,需要使用同步机制,如sync.Mutexsync.RWMutex来保护对map的访问。

  3. 生命周期:全局变量的生命周期贯穿整个程序运行过程,包括初始化、运行和退出。因此,需要特别注意在程序结束时是否需要对map中的资源进行清理,以避免内存泄漏。

  4. 命名冲突:全局变量在包内全局可见,因此命名时要避免与其他变量名冲突,建议使用具有描述性的命名方式。

总之,在使用全局map变量时,务必注意初始化、并发访问控制、生命周期管理和命名规范,以确保程序的正确性和稳定性。希望这些建议能对你有所帮助。如果还有其他问题,欢迎继续提问。

回到顶部