Table of Contents
Introduction: Understanding the synchronization requirements in concurrency
sync.WaitGroup core mechanism
Example of usage: Wait for multiple tasks to complete
Notes and best practices
Summarize
Home Backend Development Golang Go language concurrent programming: mastering the synchronization mechanism of sync.WaitGroup

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

Aug 26, 2025 am 10:06 AM

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 of this Website
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

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

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

PHP Tutorial
1600
276
go by example http middleware logging example go by example http middleware logging example Aug 03, 2025 am 11:35 AM

HTTP log middleware in Go can record request methods, paths, client IP and time-consuming. 1. Use http.HandlerFunc to wrap the processor, 2. Record the start time and end time before and after calling next.ServeHTTP, 3. Get the real client IP through r.RemoteAddr and X-Forwarded-For headers, 4. Use log.Printf to output request logs, 5. Apply the middleware to ServeMux to implement global logging. The complete sample code has been verified to run and is suitable for starting a small and medium-sized project. The extension suggestions include capturing status codes, supporting JSON logs and request ID tracking.

How do you work with environment variables in Golang? How do you work with environment variables in Golang? Aug 19, 2025 pm 02:06 PM

Goprovidesbuilt-insupportforhandlingenvironmentvariablesviatheospackage,enablingdeveloperstoread,set,andmanageenvironmentdatasecurelyandefficiently.Toreadavariable,useos.Getenv("KEY"),whichreturnsanemptystringifthekeyisnotset,orcombineos.Lo

go by example running a subprocess go by example running a subprocess Aug 06, 2025 am 09:05 AM

Run the child process using the os/exec package, create the command through exec.Command but not execute it immediately; 2. Run the command with .Output() and catch stdout. If the exit code is non-zero, return exec.ExitError; 3. Use .Start() to start the process without blocking, combine with .StdoutPipe() to stream output in real time; 4. Enter data into the process through .StdinPipe(), and after writing, you need to close the pipeline and call .Wait() to wait for the end; 5. Exec.ExitError must be processed to get the exit code and stderr of the failed command to avoid zombie processes.

What is the standard project layout for a Go application? What is the standard project layout for a Go application? Aug 02, 2025 pm 02:31 PM

The answer is: Go applications do not have a mandatory project layout, but the community generally adopts a standard structure to improve maintainability and scalability. 1.cmd/ stores the program entrance, each subdirectory corresponds to an executable file, such as cmd/myapp/main.go; 2.internal/ stores private code, cannot be imported by external modules, and is used to encapsulate business logic and services; 3.pkg/ stores publicly reusable libraries for importing other projects; 4.api/ optionally stores OpenAPI, Protobuf and other API definition files; 5.config/, scripts/, and web/ store configuration files, scripts and web resources respectively; 6. The root directory contains go.mod and go.sum

How do you use conditional statements like if-else in Go? How do you use conditional statements like if-else in Go? Aug 02, 2025 pm 03:16 PM

The if-else statement in Go does not require brackets but must use curly braces. It supports initializing variables in if to limit scope. The conditions can be judged through the elseif chain, which is often used for error checking. The combination of variable declaration and conditions can improve the simplicity and security of the code.

What does the go run command do? What does the go run command do? Aug 03, 2025 am 03:49 AM

gorun is a command for quickly compiling and executing Go programs. 1. It completes compilation and running in one step, generates temporary executable files and deletes them after the program is finished; 2. It is suitable for independent programs containing main functions, which are easy to develop and test; 3. It supports multi-file operation, and can be executed through gorun*.go or lists all files; 4. It automatically processes dependencies and uses the module system to parse external packages; 5. It is not suitable for libraries or packages, and does not generate persistent binary files. Therefore, it is suitable for rapid testing during scripts, learning and frequent modifications. It is an efficient and concise way of running.

What are the alternatives to standard library logging in Golang? What are the alternatives to standard library logging in Golang? Aug 05, 2025 pm 08:36 PM

FornewGo1.21 projects,useslogforofficialstructuredloggingsupport;2.Forhigh-performanceproductionservices,chooseZaporZerologduetotheirspeedandlowallocations;3.ForeaseofuseandrichintegrationslikeSlackorSentryhooks,Logrusisidealdespitelowerperformance;4

How do you handle routing in a Go web application? How do you handle routing in a Go web application? Aug 02, 2025 am 06:49 AM

Routing in Go applications depends on project complexity. 1. The standard library net/httpServeMux is suitable for simple applications, without external dependencies and is lightweight, but does not support URL parameters and advanced matching; 2. Third-party routers such as Chi provide middleware, path parameters and nested routing, which is suitable for modular design; 3. Gin has excellent performance, built-in JSON processing and rich functions, which is suitable for APIs and microservices. It should be selected based on whether flexibility, performance or functional integration is required. Small projects use standard libraries, medium and large projects recommend Chi or Gin, and finally achieve smooth expansion from simple to complex.

See all articles