当前位置:网站首页>Explanation of waitgroup usage in go language learning
Explanation of waitgroup usage in go language learning
2022-06-11 13:24:00 【1024 Q】
Preface
A profound
The overview
Underlying implementation
Structure
Add
Done
Wait
It's easy to get wrong
summary
Preface In the previous article , We used it WaitGroup Arrange tasks ,Go In language WaitGroup and Java Medium CyclicBarrier、CountDownLatch Very similar . For example, we have a main task in execution , At a certain point, three subtasks need to be executed in parallel , And you need to wait until all three subtasks are completed , Continue to perform the main task . Then we need to set up a checkpoint , Keep the main task blocked here , Wait until the three subtasks are completed .
explain : The example in this article , All based on Go1.17 64 Bit machine
A profound Let's start with a simple example , look down WaitGroup How is it used . Use... In the example Add(5) We have 5 individual The subtasks , Then I got up 5 individual Cooperate to complete the task , Use of main coordination process Wait() Method wait The execution of the subprocess is completed , Output the total waiting time .
func main() { var waitGroup sync.WaitGroup start := time.Now() waitGroup.Add(5) for i := 0; i < 5; i++ { go func() { defer waitGroup.Done() time.Sleep(time.Second) fmt.Println("done") }() } waitGroup.Wait() fmt.Println(time.Now().Sub(start).Seconds())}/*donedonedonedonedone1.000306089*/ The overview WaitGroup There are three ways :
(wg *WaitGroup) Add(delta int)(wg *WaitGroup) Done()(wg *WaitGroup) Wait()Add Method for setting WaitGroup The count of , It can be understood as the number of subtasks
Done Method is used to WaitGroup Subtract one from the count of , It can be understood as completing a subtask
Wait Method is used to block the caller , until WaitGroup The count value of is 0, That is, all subtasks are completed
Normally , When we use it , The number of subtasks needs to be determined first , And then call Add() Method to pass in the corresponding quantity , In the coordination of each subtask , call Done(), The co process call that needs to wait Wait() Method , The status flow is shown in the following figure :

type WaitGroup struct { noCopy noCopy // noCopy Field identification , because WaitGroup Can't copy , Convenient tool detection state1 [3]uint32 // 12 Bytes ,8 Byte identification Count value and waiting quantity ,4 Bytes are used to identify semaphores }state1 Is a compound field , Will be split into two parts : 64 position (8 Bytes ) Of statep As a whole for atomic operations , In front of it 4 Bytes represent the count value , The last four bytes indicate the number of waits ; The remaining 32 position (4 Bytes )semap Used to identify semaphores .
Go In language, for 64 Bit variables to perform atomic operations , You need to ensure that the variable is 64 Bit alignment Of , That is to ensure that 8 Bytes The first address of 8 Integer multiple . So when state1 The first address of 8 The integral times of , take front 8 individual Bytes as statep , after 4 individual Bytes as semap; When state1 Your first address is not 8 The integral times of , take after 8 individual Bytes as statep , front 4 individual Bytes as semap.
func (wg *WaitGroup) state() (statep *uint64, semap *uint32) { // The first address is 8 A multiple of the , front 8 Bytes to statep, The last four bytes are semap if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 { return (*uint64)(unsafe.Pointer(&wg.state1)), &wg.state1[2] } else { // after 8 Bytes to statep, The first four bytes are semap return (*uint64)(unsafe.Pointer(&wg.state1[1])), &wg.state1[0] }}AddAdd Method is used to add a count value ( A negative number is equal to minus ), When the count changes to 0 after , Wait All waiters blocked by the method will be released
It is illegal for the count value to become negative , produce panic
When the count is 0 when ( The initial state ),Add Methods cannot be compared with Wait Method is called concurrently , Need assurance Add Method in Wait Method Before call , Otherwise panic
func (wg *WaitGroup) Add(delta int) { // Get the count value and wait for the variable statep and Semaphore semap statep, semap := wg.state() // Count value plus delta: statep The first four bytes of are count values , So it will delta Move forward 32 position state := atomic.AddUint64(statep, uint64(delta)<<32) // Count value v := int32(state >> 32) // The number of people waiting w := uint32(state) // If you add delta after , The count value becomes negative , illegal ,panic if v < 0 { panic("sync: negative WaitGroup counter") } // delta > 0 && v == int32(delta) : From 0 Start adding count values // w!=0 : It means that there are already waiting people // Explain that when adding the count value , At the same time, a waiting person is added , Illegal operation . To add a waiting person, you need to add the count value if w != 0 && delta > 0 && v == int32(delta) { panic("sync: WaitGroup misuse: Add called concurrently with Wait") } // v>0 : The count value is not equal to 0, There is no need to wake up the waiters , Go straight back to // w==0: There are no waiters , There is no need to wake up , Go straight back to if v > 0 || w == 0 { return } // Check the data again for consistency if *statep != state { panic("sync: WaitGroup misuse: Add called concurrently with Wait") } // Here, the count value is 0, And the waiting person is greater than 0, It is necessary to wake up all the waiting people , And set the system to the initial state (0 state ) // Set both the count value and the number of waiters to 0 *statep = 0 // Wake up the waiting for ; w != 0; w-- { runtime_Semrelease(semap, false, 0) }}Done// Complete a task , Subtract one from the count , When the count value is reduced to 0 when , It is necessary to wake up all the waiting people func (wg *WaitGroup) Done() { wg.Add(-1)}Wait// call Wait Methods will be blocked , until Count value Turn into 0func (wg *WaitGroup) Wait() { // Get count 、 Waiting number and semaphore statep, semap := wg.state() for { state := atomic.LoadUint64(statep) // Count value v := int32(state >> 32) // The number of people waiting w := uint32(state) // The number of counts is 0, Go straight back to , No need to wait if v == 0 { return } // This indicates that the count value is greater than 0 // Increase the number of people waiting : There will be competition , Like multiple Wait call , Or call at the same time Add Method , If the increase is unsuccessful, it will continue for loop if atomic.CompareAndSwapUint64(statep, state, state+1) { // After adding success , Block here in semaphore , Waiting to be awakened runtime_Semacquire(semap) // When awakened , Should be 0 state . If you reuse WaitGroup, Need to wait Wait return if *statep != 0 { panic("sync: WaitGroup is reused before previous Wait has returned") } return } }} It's easy to get wrong From the above analysis of the source code, we can see that several will be generated panic The point of , This is what we use WaitGroup What to pay attention to
1. The count value becomes negative
call Add The parameter value is transferred to a negative number
func main() {var wg sync.WaitGroupwg.Add(1)wg.Add(-1)wg.Add(-1)}Multiple calls Done Method
func main() { var wg sync.WaitGroup wg.Add(1) go func() { fmt.Println("test") wg.Done() wg.Done() }() time.Sleep(time.Second) wg.Wait()}2.Add and Wait Concurrent invocations
Add and Wait Concurrent invocations , It may not achieve the desired effect , even to the extent that panic. In the following example , We want to wait 3 Execute the main task after all the sub tasks are executed , But the actual situation may be that the subtask hasn't been up yet , The main task continues to be executed .
func doSomething(wg *sync.WaitGroup) { wg.Add(1) fmt.Println("do something") defer wg.Done()}func main() { var wg sync.WaitGroup for i := 0; i < 3; i++ { go doSomething(&wg) } wg.Wait() fmt.Println("main")}//main//do something//do something The right way to use , Should be calling Wait Call... First Add
func doSomething(wg *sync.WaitGroup) { defer wg.Done() fmt.Println("do something")}func main() { var wg sync.WaitGroup wg.Add(3) for i := 0; i < 3; i++ { go doSomething(&wg) } wg.Wait() fmt.Println("main")}//do something//do something//do something//main3. No wait Wait return , Just reuse it WaitGroup
func main() { var wg sync.WaitGroup wg.Add(1) go func() { fmt.Println("do something") wg.Done() wg.Add(1) }() wg.Wait()}4. Copy use
We know Go Parameter passing in language , It's all value transfer , A copy operation will occur . So you're passing... To the function WaitGroup when , Operate with a pointer .
// Wrong usage , Pointer not used func doSomething(wg sync.WaitGroup) { fmt.Println("do something") defer wg.Done()}func main() { var wg sync.WaitGroup wg.Add(3) for i := 0; i < 3; i++ { // No pointers are used here ,wg The state never changes , Lead to Wait Keep blocking go doSomething(wg) } wg.Wait() fmt.Println("main")} summary We use the source code + Example way , We learned together sync.WaitGroup Implementation logic , Some points for attention are also given , Just do the following , There's no problem :
Guarantee Add stay Wait Pre invocation
Add Negative numbers are not passed in
Don't forget to call after the task is completed Done Method , It is recommended to use defer wg.Done()
Do not copy and use WaitGroup, Function is passed with a pointer
Try not to reuse WaigGroup, Reduce the risk of problems
This is about Go Language learning WaitGroup This is the end of the detailed usage article , More about Go Language WaitGroup Please search the previous articles of software development network or continue to browse the relevant articles below. I hope you will support software development network more in the future !
边栏推荐
- [bug resolution] the form is paged to display the total data res.data total
- 2022年甘肃省安全员B证考试题模拟考试题库及在线模拟考试
- 自定义Terraform-Providers(Terraform Plugin Framework)-04
- How does Cassandra, an open source database giant, tell a "new story" in China? Face to face
- @How to resolve controller and requestmapping
- AGV机器人RFID传感器CK-G06A与西门子1200PLC应用手册
- SQL:如何用采购单销售单的数据 通过移动加权平均法 计算商品成本
- 工作总结之因为笛卡尔积问题写SQL搞了半天[害](附笛卡尔积总结)
- [background interaction] select to bind the data transferred in the background
- 五年官司终败诉,万亿爬虫大军蠢蠢欲动
猜你喜欢

How about NFT market? Why is NFT so popular? How to build NFT platform

如何写出高性能代码(四)优化数据访问

The Tree (AVL, 2-3-, 红黑,Huffman)
![[bug resolution] the form is paged to display the total data res.data total](/img/92/1ddde16d35465f8dd53ebf90e249b8.png)
[bug resolution] the form is paged to display the total data res.data total
![[arcgis] City relevance analysis](/img/f4/454266e1ed586240bce9a7f36aa52e.png)
[arcgis] City relevance analysis

cadence SPB17.4 - allegro - allegro_free_viewer

长连接简介

InfoQ 极客传媒 15 周年庆征文|移动端开发之动态排行【MUI+Flask+MongoDB】

【信号处理】数字信号处理Matlab设计附GUI界面和报告

关于uni-app 配置 APP 不显示顶部标题栏设置
随机推荐
[201] PHP exception handling - try catch finally exception handling in PHP
【backtrader源码解析46】cerebro.py代码注释(枯燥,backtrader核心代码之一,推荐阅读,注释仅供参考)
tf. Data (II) -- parallelization tf data. Dataset generator
Ecplise cannot connect to SQL Server
Nomad application scheduling scheme 08 of hashicopy (detection job)
看不懂Kotlin源码?从Contracts 函数说起~
kubernetes 二进制安装(v1.20.15)(七)加塞一个工作节点
使用华为HECS云服务器打造Telegraf+Influxdb+Grafana 监控系统【华为云至简致远】
[background interaction] select to bind the data transferred in the background
2022工具钳工(中级)操作证考试题库及答案
Search without data after paged browsing
31W contest question bonus! When AI for Science collides with the "pilot Cup", what sparks will be generated?
TeaTalk·Online 演讲实录 | 圆满完结!安全上云,选对数据迁移策略很重要
漫谈软件缺陷与漏洞
Hashicopy之nomad应用编排方案08(检测Job)
Unity 检测对象是否在相机的视锥体范围内
Add environment path
JDBC connection pool is used for batch import. 5million data are run each time, but various problems will occur in the middle
启牛商学院给的券商账户是安全的吗?开户收费吗
From real-time computing to streaming data warehouse, where will Flink go next?