CodeNebula
C++20stladvanced

std::latch (C++20 Addition)

C++20 · The Big Four · Compiler flag: -std=c++20

Explanation

A single-use synchronization primitive with a counter that threads can decrement. Threads waiting on the latch block until the counter reaches zero. Cannot be reset once triggered.

Real-World Scenario

Wait for all worker threads to complete initialization before starting the main processing loop.

Quick Snippet

snippet.cpp
#include <latch>
#include <thread>
#include <vector>

std::latch ready(3);

auto worker = [&](int id) {
    // initialize...
    ready.count_down();  // signal ready
};

std::vector<std::jthread> threads;
for (int i = 0; i < 3; ++i) threads.emplace_back(worker, i);
ready.wait();  // blocks until all 3 workers are ready

Full Example

latch.cpp
// CodeNebula - C++20 Example: std::latch (One-shot Barrier)
// Compile: g++ -std=c++20 -Wall -Wextra -pthread -o example latch.cpp

#include <iostream>
#include <latch>
#include <string>
#include <thread>
#include <vector>

int main() {
    std::cout << "=== std::latch - one-shot synchronization ===\n";
    std::cout << "(A latch counts down from N; threads wait until it reaches 0)\n\n";

    constexpr int num_workers = 4;

    // Latch initialized with count = num_workers
    std::latch work_done(num_workers);

    std::cout << "--- Workers starting ---\n";
    std::vector<std::jthread> workers;
    for (int i = 0; i < num_workers; ++i) {
        workers.emplace_back([&work_done, i] {
            // Simulate varying work time
            std::this_thread::sleep_for(std::chrono::milliseconds(20 * (i + 1)));
            std::cout << "Worker " << i << " finished its task\n";
            work_done.count_down();  // Decrement the latch counter
        });
    }

    // Main thread waits until all workers are done
    work_done.wait();
    std::cout << "All workers done! Main thread continues.\n";

    for (auto& w : workers) w.join();

    std::cout << "\n=== Latch as a start gate ===\n";

    std::latch start_gate(1);  // All threads wait on this
    std::latch finish_line(3); // Main waits on this

    auto runner = [&](const std::string& name) {
        std::cout << name << " ready at start line\n";
        start_gate.wait();  // Wait for the start signal
        std::cout << name << " running!\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
        std::cout << name << " finished!\n";
        finish_line.count_down();
    };

    std::jthread r1(runner, "Alice");
    std::jthread r2(runner, "Bob");
    std::jthread r3(runner, "Carol");

    std::this_thread::sleep_for(std::chrono::milliseconds(50));
    std::cout << "GO!\n";
    start_gate.count_down();  // Release all runners

    finish_line.wait();  // Wait for all to finish
    std::cout << "All runners crossed the finish line!\n";

    std::cout << "\n=== arrive_and_wait (count_down + wait) ===\n";

    std::latch sync(3);
    auto task = [&sync](int id) {
        std::this_thread::sleep_for(std::chrono::milliseconds(10 * id));
        std::cout << "Task " << id << " arriving and waiting\n";
        sync.arrive_and_wait();  // Atomic count_down + wait
        std::cout << "Task " << id << " proceeding\n";
    };

    std::jthread t1(task, 1);
    std::jthread t2(task, 2);
    std::jthread t3(task, 3);

    return 0;
}
Run command
g++ -std=c++20 -Wall -Wextra -pthread -o example latch.cpp