golang for 循环 + 协程没用?

本文通过实例解析了Go语言中协程使用的常见误区,并分享了一种有效的优化方案,将原本耗时100秒的任务缩短至15秒,显著提升了程序效率。

问题

在写巡检脚本的时候,遇到了如下问题,特此记录。

最开始写的代码如下:

{
    var wg sync.WaitGroup
    for _, a := range activeList {
        wg.Add(1)
    	go func(ia *model.ImportantActivity) {
    		defer wg.Done()
    		if err := GetBusinessData(ia); err != nil {
    			log.Error(err)
    		}
    	}(a)
    	wg.Wait()
    }
}

整个执行下来需要100+ s ,跟没使用协程用了差不多的时间,一点效果没有。

观察自己写的协程好像也没有问题呀,但是什么好像并没有生效呢。

解决

偶然间看到了这段话,突然发现,我这里好像写的是假协程。因为如果把 wg.Add(1) 写在 for 循环里面的话,每次都要等一个for循环结束了,才会创建下一个协程,所以跟没使用协程效果一样。

golang是值拷贝传递。for循环很快,协程创建需要的时间大于for循环的时间。因为协程创建 需要进行 堆栈分配,上下文准备,以及与内核态的线程进行映射工作等。

golang for 循环创建协程问题 https://blog.youkuaiyun.com/gu864852213/article/details/119256885

对协程进一步理解后,修改如下:

{
    var wg sync.WaitGroup
    wg.Add(len(activeList))
    for _, a := range activeList {
    	go func(ia *model.ImportantActivity) {
    		defer wg.Done()
    		if err := GetBusinessData(ia); err != nil {
    			log.Error(err)
    		}
    	}(a)
    }
    wg.Wait()
}

整个只执行了 15s。

总结

    • wg.Add 和 wg.Wait 需要写在 for 循环外面,表示开启多少个协程,并等待协程都执行结束
  • 使用 闭包 进行值传递,这样不会产生变量混乱

Go语言协程使用最佳实践 https://zhuanlan.zhihu.com/p/374464199
记一次golang经典错误–for循环中的go协程调用 https://baorongquan.github.io/2018/05/06/%E8%AE%B0%E4%B8%80%E6%AC%A1golang%E7%BB%8F%E5%85%B8%E9%94%99%E8%AF%AF-for%E5%BE%AA%E7%8E%AF%E4%B8%AD%E7%9A%84go%E5%8D%8F%E7%A8%8B%E8%B0%83%E7%94%A8/

在Go语言中,goroutine(协程)的退出通常是依赖于其内部的逻辑控制,而是从外部直接强制退出的。然而,可以通过一些方法和技巧来间接控制goroutine的退出。以下是几种常见的方法: 1. **使用通道(Channel)**: 通过通道发送信号来通知goroutine退出。 ```go package main import ( "fmt" "time" ) func worker(stopChan chan bool) { for { select { case <-stopChan: fmt.Println("Received stop signal. Exiting goroutine.") return default: fmt.Println("Working...") time.Sleep(time.Second) } } } func main() { stopChan := make(chan bool) go worker(stopChan) time.Sleep(3 * time.Second) stopChan <- true // 等待goroutine退出 time.Sleep(1 * time.Second) fmt.Println("Main function exiting.") } ``` 2. **使用上下文(Context)**: 使用`context`包来传递取消信号。 ```go package main import ( "context" "fmt" "time" ) func worker(ctx context.Context) { for { select { case <-ctx.Done(): fmt.Println("Received cancel signal. Exiting goroutine.") return default: fmt.Println("Working...") time.Sleep(time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) go worker(ctx) time.Sleep(3 * time.Second) cancel() // 等待goroutine退出 time.Sleep(1 * time.Second) fmt.Println("Main function exiting.") } ``` 3. **使用`sync.WaitGroup`**: 通过`WaitGroup`来等待goroutine完成,但直接控制其退出。 ```go package main import ( "fmt" "sync" "time" ) func worker(wg *sync.WaitGroup) { defer wg.Done() for { fmt.Println("Working...") time.Sleep(time.Second) } } func main() { var wg sync.WaitGroup wg.Add(1) go worker(&wg) time.Sleep(3 * time.Second) wg.Wait() fmt.Println("Main function exiting.") } ``` 这种方法直接控制goroutine的退出,而是等待其完成。通常需要结合其他方法使用,如在goroutine内部添加退出条件。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值