前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发编程时遇到的问题

并发编程时遇到的问题

原创
作者头像
有财君
发布2023-03-22 08:54:19
4800
发布2023-03-22 08:54:19
举报

在完成一个需求时,我发现有个函数是这样写的:

代码语言:go
复制
func test(names []string) {
	for _, name := range names {
		doSomething(name)
	}
}

观察逻辑发现这个数组中的每个元素执行起来,其实没有必然的先后关系,完全可以并发执行,于是我改了一版代码:

代码语言:go
复制
func test(names []string) {
	for _, name := range names {
		go func() {
			doSomething(name)
		}
	}
	time.Sleep(time.Second)
}

执行后令我疑惑的事情发生了,假设names="Zhao", "Qian", "Sun",那么执行的结果里只有"Sun"的记录。

由于当时刚接触Golang,想了半天也不知道为什么。但是后来我自己想明白了,for循环属于主goroutine,主goroutine才不会管其他goroutine是否被执行了。而我的goroutine是在循环结束的时候才执行的,这个时候的name就一定已经是"Sun"了。

比如这段代码,接近100%的概率什么都不会打印出来,这就是因为主goroutine只会自顾自的执行自己的工作,结束后则退出,那个时候一切都结束了:

代码语言:go
复制
fun main() {
	go fmt.Println("Hello")
}

接下来做了另外一版的修改,这样就很完美了:

代码语言:go
复制
func test(names []string) {
	for _, name := range names {
		go func(n string) {
			doSomething(n)
		}(name)
	}
	time.Sleep(time.Second)
}

将name作为一个参数传入闭包中。此时函数内的name不会受到外部影响,这样就可以执行出正确的结果了。

这里有一点需要注意,由于name是string类型的,属于非引用类型,在当做参数被传入的时候,是会将其复制一份传入的,此时的入参就成了完全独立的存在,不受外部影响。

不过这个代码实现给我的感觉依旧很愚蠢,因为加上了一个sleep方法。如果有一个name的执行时间(或者调用接口网络抖动)超过了1s,当然主goroutine还是不会等它执行完成就会退出,会导致一些不可预见的问题发生。

总不可能无限制的增加sleep时长来换取安全性。实际上这些names是可以协同等待的,等待所有的goroutine执行结束之后,一起退出。这种模式有点类似于Java的CountDownLatch,在Golang中可以借助sync.WaitGroup来实现。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档