Open In App

C++ 20 – <latch> Header

Last Updated : 15 May, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In C++20, the header was introduced, which allows a synchronization primitive known as a latch. Latches allow one or more threads to wait until a certain number of operations have been finished before proceeding.

A synchronization object called a latch is initialized with a preset count value. This object enables threads to pause until the count is zero. If count_down() is invoked, the latch’s count is reduced by one. After the count has reached zero, any threads that were awaiting the latch will be freed and able to continue.

Syntax:

#include <latch>

The std::latch class has four member functions

  • explicit latch(std::ptrdiff_t count): Constructs a new latch object with an initial count of the count.
  • void count_down(std::ptrdiff_t n = 1): Subtract n from the latch count. Whenever the count reaches zero, all of the threads that are waiting on the latch become unblocked.
  • bool try_wait() const noexcept: Verifies if the tally has hit zero. In that eventuality, the output is true. If not, there will be no holdup in returning false.
  • void wait() const: Blocks the calling thread until the latch count reaches zero.

Note: Below program is used in C++14 version which will give us error. The header <latch> was added to the C++ Standard Library in C++20, so if you are using a compiler that supports only earlier versions of C++, such as C++11, C++14 or C++17, you won’t be able to use it. To fix this error, you can either upgrade your compiler to one that supports C++20 or use a different synchronization primitive, such as a barrier or a condition variable, to achieve the same functionality.

Examples of the <latch> Header

Example 1: 

C++14




// C++ Program to demonstrate Multithreading with latch
#include <iostream>
#include <latch>
#include <thread>
  
void worker(std::latch& latch)
{
    std::cout << "Worker thread started" << std::endl;
  
    // do some work
    std::cout << "Worker thread finished" << std::endl;
  
    // decrement the latch count
    latch.count_down();
}
  
int main()
{
    const int num_threads = 3;
    std::latch latch(num_threads);
  
    for (int i = 0; i < num_threads; ++i) {
        std::thread t(worker, std::ref(latch));
        t.detach();
    }
  
    // wait until all threads have finished
    latch.wait();
    std::cout << "All worker threads finished" << std::endl;
    return 0;
}


Output:

Worker thread finished
Worker thread started
Worker thread finished
All worker threads finished

Explanation of the above program

We establish a latch that has a count equal to the number of threads and assign it as a reference to every worker thread in this particular example. Once each worker thread completes its task, it executes the count_down() function on the latch. Eventually, the main thread invokes the wait() function on the latch, suspending execution until the latch’s count reduces to zero, implying that all worker threads have executed count_down(). Upon reaching zero, the primary thread becomes unblocked and emits a notification signifying the completion of all worker threads.

Example 2:  

C++14




// C++ Program to demonstrate use of producer-consumer
// Problem
#include <iostream>
#include <latch>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
  
const int kNumProducers = 2;
const int kNumConsumers = 2;
const int kNumItemsPerProducer = 5;
  
std::queue<int> gQueue;
std::mutex gMutex;
std::latch gLatch(kNumProducers);
  
void Producer(int id)
{
    for (int i = 0; i < kNumItemsPerProducer; ++i) {
        std::scoped_lock lock(gMutex);
        gQueue.push(id * kNumItemsPerProducer + i);
    }
    gLatch.count_down();
}
  
void Consumer(int id)
{
    gLatch.wait();
    while (true) {
        std::scoped_lock lock(gMutex);
        if (!gQueue.empty()) {
            int item = gQueue.front();
            gQueue.pop();
            std::cout << "Consumer " << id << " got item "
                      << item << std::endl;
        }
        else {
            break;
        }
    }
}
  
int main()
{
    std::vector<std::thread> producers;
    std::vector<std::thread> consumers;
  
    for (int i = 0; i < kNumProducers; ++i) {
        producers.emplace_back(Producer, i);
    }
  
    for (int i = 0; i < kNumConsumers; ++i) {
        consumers.emplace_back(Consumer, i);
    }
  
    for (auto& t : producers) {
        t.join();
    }
  
    for (auto& t : consumers) {
        t.join();
    }
  
    return 0;
}


Output:

Consumer 1 got item 1
Consumer 1 got item 2
Consumer 1 got item 3
Consumer 1 got item 4


Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads