search
HomeBackend DevelopmentGolangGo language concurrent programming: mastering the synchronization mechanism of sync.WaitGroup

Go language concurrent programming: master the synchronization mechanism of sync.WaitGroup

sync.WaitGroup is an important tool for coroutine synchronization in Go language. It allows the main coroutine to wait for a group of child coroutines to complete execution. By adding the counter, Done reduces the counter, and Wait blocking until the counter is zeroed, WaitGroup ensures the orderly completion of concurrent tasks, which is the key to building robust concurrent applications.

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:

  1. 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.
  2. 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.
  3. 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

  1. 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.
  2. 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.
  3. 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.
  4. 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!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Go language template rendering result capture: use bytes.Buffer to efficiently obtain string outputGo language template rendering result capture: use bytes.Buffer to efficiently obtain string outputAug 27, 2025 am 11:48 AM

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.

Running Go programs on Android: ARM Architecture Compilation and Deployment GuideRunning Go programs on Android: ARM Architecture Compilation and Deployment GuideAug 27, 2025 am 11:36 AM

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.

Rendering data in a struct in GAE using a Go template (using slices)Rendering data in a struct in GAE using a Go template (using slices)Aug 27, 2025 am 11:33 AM

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.

Use Go to handle cleanup before exitUse Go to handle cleanup before exitAug 27, 2025 am 11:30 AM

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.

Installation and Getting Started on the Go language on Windows PlatformInstallation and Getting Started on the Go language on Windows PlatformAug 27, 2025 am 11:27 AM

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.

Google App Engine multilingual application deployment and management: implementing a hybrid architectureGoogle App Engine multilingual application deployment and management: implementing a hybrid architectureAug 27, 2025 am 11:15 AM

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.

Building a parser: From Getting Started to PracticeBuilding a parser: From Getting Started to PracticeAug 27, 2025 am 11:12 AM

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.

Working with 2D arrays for web development using Go templatesWorking with 2D arrays for web development using Go templatesAug 27, 2025 am 11:06 AM

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.

See all articles

Hot AI Tools

Undress AI Tool

Undress AI Tool

Undress images for free

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

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

Hot Tools

Safe Exam Browser

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

SublimeText3 English version

Recommended: Win version, supports code prompts!

SublimeText3 Linux new version

SublimeText3 Linux new version

SublimeText3 Linux latest version

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools

Hot Topics