


Go language concurrent programming: mastering the synchronization mechanism of sync.WaitGroup
Introduction: Understanding the synchronization requirements in concurrency
In Go language, goroutines are lightweight concurrent execution units, making writing concurrent programs simple and efficient. However, when the main coroutine starts multiple child coroutines to perform tasks, a common requirement is that the main coroutine needs to complete its tasks before continuing to perform subsequent operations. For example, if multiple worker coroutines are started to process data, the main coroutine needs to wait for all data to be processed before summarizing the results or exiting. sync.WaitGroup is a synchronization primitive designed to solve such scenarios as "waiting for all concurrent tasks to complete".
It should be noted that the main purpose of sync.WaitGroup is to wait for the completion of a set of coroutines, which is different from the function of sync.Mutex (mutex). sync.Mutex is used to protect shared resources, ensuring that only one coroutine can access the resource at the same time to avoid data race. sync.WaitGroup focuses on the life cycle management of coroutines and the synchronization of task completion.
sync.WaitGroup core mechanism
WaitGroup maintains a counter internally, and its operations are mainly implemented through the following three methods:
-
Add(delta int) :
- Function: Increase the internal counter of WaitGroup.
- Timing: It is usually called before a new coroutine is started, or when a task is added dynamically inside the coroutine. The delta parameter is usually a positive number, indicating the number of coroutines or tasks to be waited for. Each call to Add(1) means adding a task to be completed.
- Importance: Ensure that before Wait() is called, all tasks that need to be waited have been "registered" into the WaitGroup.
-
Done() :
- Function: Reduce WaitGroup's internal counter.
- Time: This method should be called after each coroutine completes its task.
- Best practice: To ensure that the counts are correctly reduced even if a coroutine occurs, the defer wg.Done() statement is usually used.
-
Wait() :
- Function: Block the current coroutine until the internal counter of WaitGroup is zeroed.
- Time: Usually called in the main coroutine to wait for all child coroutines to complete. Once the counter becomes zero, the Wait() method will unblock and the main coroutine will continue to execute.
Example of usage: Wait for multiple tasks to complete
The following is a simple example to demonstrate the usage of sync.WaitGroup. We will simulate starting multiple concurrent workers (goroutines), and the main program needs to wait for all workers to complete their tasks before continuing to execute.
package main import ( "fmt" "sync" "time" ) // The worker function simulates a concurrent task func worker(id int, wg *sync.WaitGroup) { // defer wg.Done() ensures that before the worker function exits, whether it ends normally or panic occurs. // Wg.Done() will be called to reduce the WaitGroup counter. defer wg.Done() fmt.Printf("Worker %d: Task start...\n", id) time.Sleep(time.Duration(id) * time.Second) // Simulation work takes time fmt.Printf("Worker %d: task completed.\n", id) } func main() { var wg sync.WaitGroup // Declare a WaitGroup variable numWorkers := 3 // The number of workers we want to start fmt.Println("Main coroutine: Start worker...") for i := 1; i <p> <strong>Code explanation:</strong></p>
- In the main function, we first declare a variable wg of type sync.WaitGroup. The zero value of WaitGroup is available without explicit initialization.
- Through a loop, we start numWorkers worker coroutines. Before each go worker(...), we call wg.Add(1), which means we expect a new coroutine to join the waiting queue, and the WaitGroup counter will increase as a result.
- The worker function receives an id and a *sync.WaitGroup pointer. At the beginning of the function body, we use defer wg.Done(). This means that when the worker function is executed (whether it is normal return or because of panic), wg.Done() will be called, decrementing the WaitGroup counter by 1.
- After starting all worker coroutines in the main function, we call wg.Wait(). This call blocks the main coroutine until the internal counter of WaitGroup becomes zero. When all worker coroutines call wg.Done(), the counter becomes zero, wg.Wait() unblocks, the main coroutine continues to execute, and prints out the message "all workers have completed".
Notes and best practices
-
Add's call timing :
- Add must be called before Wait, or when Wait is called, the WaitGroup counter has not yet been reset to zero.
- If Add is called after Wait or after the counter has been zeroed, it may cause Wait to fail to block again, or raise panic (if the counter is 0 and Add is called again). For example, calling Add again on a WaitGroup may result in race conditions or logical errors after all waits have been completed and has been unblocked by Wait.
-
Done's Match :
- Make sure that each Add operation has a corresponding Done operation. If Done calls less than Add, Wait will block forever; if Done calls more than Add, it will cause the counter to become negative, causing panic.
- Using defer wg.Done() is a best practice to prevent missed Done calls, especially if a function may return early or an error occurs.
-
WaitGroup delivery :
- sync.WaitGroup is a value type, but is usually passed to the coroutine as a pointer (*sync.WaitGroup). This is because the internal state of WaitGroup is shared, and if passed by value, each coroutine will get an independent copy of WaitGroup and the counters between them will not be synchronized.
-
Zero value available :
- The zero value of sync.WaitGroup is available without explicit initialization (e.g. wg := sync.WaitGroup{} or var wg sync.WaitGroup).
Summarize
sync.WaitGroup is a powerful tool for managing the life cycle of concurrent coroutines in Go. It provides a simple and efficient way to enable the main program to reliably wait for all child coroutines to complete their tasks. By rationally using the three core methods of Add, Done and Wait, developers can ensure the orderly completion of concurrent tasks, avoid problems such as race conditions or early exit of programs, thereby building a more robust and efficient Go concurrent application. Mastering WaitGroup is the basis for writing high-quality Go concurrent programs.
The above is the detailed content of Go language concurrent programming: mastering the synchronization mechanism of sync.WaitGroup. For more information, please follow other related articles on the PHP Chinese website!

A common mistake is that the custom io.Writer implementation fails to correctly accumulate data when it is necessary to get the string result after the template is rendered in Go instead of writing directly to the HTTP response. This article will analyze this problem in depth, point out common pitfalls in custom io.Writer implementations, and provide the standard library bytes.Buffer as an efficient and reliable solution, showing how to use it to capture template output and convert it to strings to ensure data integrity, thereby avoiding data truncation issues caused by improper writing methods.

This article will provide detailed instructions on how to run a command-line program written in Go on Android devices. The core steps include leveraging the powerful cross-compilation capability of Go language, compiling Go source code into an Android executable for ARM architecture, and then transferring it to an Android device and executing it. This process does not require a complex Android development environment, and is suitable for deploying Go backend services or command line tools.

This article describes how to use templates to render data in structures in Google App Engine (GAE) Go applications, with a focus on using slices instead of container/vector packages. With sample code and detailed explanations, it helps developers understand how to access and present sliced data in structures in templates, and provides some best practice suggestions.

This article will explain how to capture interrupt signals (such as Ctrl C) in a Go program and perform necessary cleaning operations before the program exits. By listening to the os.Interrupt signal, we can ensure that the program can complete critical tasks such as closing files, releasing resources, saving state when exiting, thereby avoiding data loss or inconsistent state problems.

This tutorial is intended to guide users in detail how to install the Go language compiler and development environment on the Windows operating system, and to demonstrate how to compile and run Go programs. The content covers official download channels, installation steps, environment variable configuration, installation verification, and the writing and execution of basic code, helping you quickly start your Go language programming journey.

This tutorial explores how to deploy multilingual hybrid applications on Google App Engine (GAE). GAE allows the deployment of "services" or "versions" in different languages under the same application ID, each service or version is accessed through an independent URL, thereby achieving modular integration of different technology stacks. This approach avoids splitting applications into completely independent entities, providing developers with the powerful ability to build flexible, scalable hybrid architectures.

This article aims to provide basic concepts and guidance for building parsers. A parser is a key tool for converting strings into structured data. This article will introduce the basic principles of parsers and provide learning resources to help readers understand core concepts such as lexical analysis, recursive degradation analysis, and top-down parsing, and ultimately be able to build a custom parser, such as parsing nested key-value pair structures.

This article describes how to use the text/template package to process structure data containing two-dimensional arrays in Go Web development. Through sample code, we show how to iterate over two-dimensional arrays using range and render data in HTML templates to achieve the requirements of dynamically generating complex layouts such as tables. At the same time, it also provides iterative methods that may be used in old template packages, providing developers with a more comprehensive reference.


Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Safe Exam Browser
Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.

SublimeText3 English version
Recommended: Win version, supports code prompts!

SublimeText3 Linux new version
SublimeText3 Linux latest version

Dreamweaver CS6
Visual web development tools

WebStorm Mac version
Useful JavaScript development tools