go waitgroup 使用 cond
在Go语言中,sync.WaitGroup
是一种用于等待一组协程完成的同步机制。它提供了一种简便的方法来确保多个协程全部执行完毕后再继续执行主程序或其他操作。以下是sync.WaitGroup
的基本使用方法:
- 创建WaitGroup:首先,你需要创建一个
WaitGroup
实例。 - 添加计数:使用
Add
方法为WaitGroup添加计数,通常在启动新的协程之前调用。 - 在协程中调用Done:每个协程在完成任务时调用
Done
方法,表示它的工作已经完成。 - 等待所有协程完成:使用
Wait
方法阻塞主协程,直到所有计数器都为零。
下面是一个具体的示例代码,演示了如何使用sync.WaitGroup
:
package main import ( "fmt" "sync" "time" ) func worker(id int, wg *sync.WaitGroup) { defer wg.Done() // 在协程完成时调用Done fmt.Printf("Worker %d starting\n", id) // 模拟一些工作 time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup for i := 1; i <= 5; i++ { wg.Add(1) // 启动一个新的协程前增加计数 go worker(i, &wg) } wg.Wait() // 等待所有协程完成 fmt.Println("All workers done") }
在这个示例中:
- 我们创建了一个
sync.WaitGroup
实例wg
。 - 在启动每个
worker
协程之前,调用wg.Add(1)
来增加计数。 - 每个
worker
协程完成任务时,调用wg.Done()
来减少计数。 - 主协程调用
wg.Wait()
来等待所有协程完成。当所有worker
都调用了Done
并且计数器减到零时,wg.Wait()
解除阻塞,主协程继续执行。
这种机制非常适用于需要协调多个并发任务的情况,确保所有任务都完成后再执行后续操作。
package main import ( "fmt" "sync" "time" ) func worker(id int, wg *sync.WaitGroup) { wg.Add(1) // 会崩溃 defer wg.Done() // 在协程完成时调用Done fmt.Printf("Worker %d starting\n", id) // 模拟一些工作 time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) } func main() { var wg sync.WaitGroup for i := 1; i <= 5; i++ { // wg.Add(1) // 启动一个新的协程前增加计数 go worker(i, &wg) } wg.Wait() // 等待所有协程完成 fmt.Println("All workers done") }
https://studygolang.com/articles/12972
0x01 介绍
经常会看到以下了代码:
1
|
package main
|
主线程为了等待goroutine都运行完毕,不得不在程序的末尾使用time.Sleep()
来睡眠一段时间,等待其他线程充分运行。对于简单的代码,100个for循环可以在1秒之内运行完毕,time.Sleep()
也可以达到想要的效果。
但是对于实际生活的大多数场景来说,1秒是不够的,并且大部分时候我们都无法预知for循环内代码运行时间的长短。这时候就不能使用time.Sleep()
来完成等待操作了。
可以考虑使用管道来完成上述操作:
1
|
func main() {
|
首先可以肯定的是使用管道是能达到我们的目的的,而且不但能达到目的,还能十分完美的达到目的。
但是管道在这里显得有些大材小用,因为它被设计出来不仅仅只是在这里用作简单的同步处理,在这里使用管道实际上是不合适的。而且假设我们有一万、十万甚至更多的for循环,也要申请同样数量大小的管道出来,对内存也是不小的开销。
对于这种情况,go语言中有一个其他的工具sync.WaitGroup
能更加方便的帮助我们达到这个目的。
WaitGroup
对象内部有一个计数器,最初从0开始,它有三个方法:Add(), Done(), Wait()
用来控制计数器的数量。Add(n)
把计数器设置为n
,Done()
每次把计数器-1
,wait()
会阻塞代码的运行,直到计数器地值减为0。
使用WaitGroup
将上述代码可以修改为:
1
|
func main() {
|
这里首先把wg
计数设置为100, 每个for循环运行完毕都把计数器减一,主函数中使用Wait()
一直阻塞,直到wg为零——也就是所有的100个for循环都运行完毕。相对于使用管道来说,WaitGroup
轻巧了许多。
0x02 注意事项
1. 计数器不能为负值
我们不能使用Add()
给wg
设置一个负值,否则代码将会报错:
1
|
panic: sync: negative WaitGroup counter
|
同样使用Done()
也要特别注意不要把计数器设置成负数了。
2. WaitGroup对象不是一个引用类型
WaitGroup对象不是一个引用类型,在通过函数传值的时候需要使用地址:
1
|
func main() {
|
chan 实现相同功能
package main import ( "fmt" "time" ) func worker(id int, done chan<- bool) { fmt.Printf("Worker %d starting\n", id) // 模拟一些工作 time.Sleep(time.Second) fmt.Printf("Worker %d done\n", id) // 任务完成后发送信号 done <- true } func main() { const numWorkers = 5 done := make(chan bool, numWorkers) for i := 1; i <= numWorkers; i++ { go worker(i, done) } // 等待所有协程完成 for i := 1; i <= numWorkers; i++ { <-done } fmt.Println("All workers done") }
chan 还可以限制,同一时间,go 处理的数量
cond :
Cond 条件变量用于在满足特定条件之前阻塞或唤醒一个或多个协程。 package main import ( "fmt" "sync" "time" ) func main() { var mu sync.Mutex cond := sync.NewCond(&mu) var ready bool go func() { time.Sleep(time.Second) // 模拟一些工作 mu.Lock() ready = true cond.Signal() // 通知一个等待的协程 mu.Unlock() }() mu.Lock() for !ready { cond.Wait() // 阻塞直到条件满足 } fmt.Println("Condition met, continuing...") mu.Unlock() }
package main import ( "fmt" "sync" "time" ) func main() { var mu sync.Mutex cond := sync.NewCond(&mu) var ready bool go func() { time.Sleep(time.Second) // 模拟一些工作 mu.Lock() fmt.Println("i am lock routine") ready = true cond.Signal() // 通知一个等待的协程 mu.Unlock() }() mu.Lock() fmt.Println("i am lock") for !ready { cond.Wait() // 阻塞直到条件满足 } fmt.Println("Condition met, continuing...") mu.Unlock() } output: i am lock i am lock routine Condition met, continuing... Program exited.
Once
Once
确保某些初始化代码只执行一次。
package main import ( "fmt" "sync" ) func main() { var once sync.Once var initValue int initialize := func() { fmt.Println("Initializing...") initValue = 42 } var wg sync.WaitGroup const numWorkers = 5 for i := 0; i < numWorkers; i++ { wg.Add(1) go func(id int) { defer wg.Done() once.Do(initialize) // 只执行一次 fmt.Printf("Worker %d, initValue: %d\n", id, initValue) }(i) } wg.Wait() }
参考:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2022-06-06 tmp 20220606
2022-06-06 pv vg lv