Multithreaded programming is inherently difficult. One of the reasons is that we can't have control over how a thread will start and finish - in which order - it all depends upon the thread scheduling algorithm of the OS. This makes the reproduction of test cases difficult. Moreover, there are race conditions and deadlocks.
When I was teaching the Countdown latch - a thread synchronization technique used in the Java Concurrency package, there was none like that available in C++. I am happy to see that the concept of latch is introduced in C++20.
So...
What is a Latch in C++?
- A synchronization primitive was introduced in C++20.
- It allows one or more threads to wait for a certain number of operations to complete before proceeding.
- Acts like a countdown counter that blocks threads until it reaches zero.
Good to know that the C++ team is trying to catch up with Java...
Here we go...
My experimentation with C++ latches.
I taught my young son Ridit about the Java Countdown latch three years ago.
Good to see the C++ team is making the standard library more powerful day by day.
Here is the C++ source code of my experimentation.
The following C++ code needs C++20 to compile and execute.
/*
* Student.h
*
* Created on: Jan 7, 2024
* Author: som
*/
#ifndef STUDENT_H_
#define STUDENT_H_
#include <iostream>
#include <string>
#include <thread>
#include <latch>
#include <chrono>
#include <vector>
#include <syncstream> // For thread-safe console output
#include <format> // For C++20 string formatting
class Student {
private:
std::string name;
int timeToFinish;
public:
Student(std::string name, int finish)
: name(std::move(name)),
timeToFinish(finish) {}
virtual ~Student() = default;
void task(std::latch& l){
auto now = std::chrono::system_clock::now();
// Locate the current system timezone and convert UTC to local time
auto local_time = std::chrono::current_zone()->to_local(now);
std::osyncstream(std::cout)
<< name << " is Starting the task at "
<< std::format("{:%F %T}", local_time) << "\n";
// 4. Simulate performing the task
std::this_thread::sleep_for(std::chrono::milliseconds(timeToFinish));
std::osyncstream(std::cout) << name << " finished the task.\n";
l.count_down();
}
};
#endif /* STUDENT_H_ */
/*
* classETC.h
*
* Created on: Jan 7, 2024
* Author: som
*/
/*
* classETC.h
*
* Created on: Jan 7, 2024
* Author: som
*/
#ifndef CLASSETC_H_
#define CLASSETC_H_
#include "Student.h"
class classETC {
public:
Student ridit {"Ridit", 1000};
Student ishan {"Ishan", 3000};
Student rajdeep {"Rajdeep", 900};
classETC() = default;
virtual ~classETC() = default;
auto giveTaskToStudent(std::latch& l){
std::vector<std::jthread> workers;
workers.reserve(3);
// Emplace threads directly into the vector
workers.emplace_back(&Student::task, &this->ridit, std::ref(l));
workers.emplace_back(&Student::task, &this->ishan, std::ref(l));
workers.emplace_back(&Student::task, &this->rajdeep, std::ref(l));
std::cout<<"Teacher is waiting for all the students to finish their task"<<std::endl;
l.wait();
std::cout<<"All students submitted their task... Teacher is leaving the class"<<std::endl;
return workers; // Named Return Value Optimization (NRVO) handles this cleanly
}
};
#endif /* CLASSETC_H_ */
Main:
// ==========================================
// Main Method
// ==========================================
#include "classETC.h"
int main() {
// Print C++ standard validation
std::cout << "C++ Version: ";
if (__cplusplus == 202101L) std::cout << "C++23\n";
else if (__cplusplus == 202002L) std::cout << "C++20\n";
else std::cout << "Other/Experimental (" << __cplusplus << ")\n";
std::cout << "-------------------------------------------\n";
std::latch l(3);
classETC etc;
// FIX: Capturing the threads in main() preserves their lifetimes.
// They will execute concurrently and automatically join when main() ends.
auto threads = etc.giveTaskToStudent(l);
return 0;
}
Explanation of the code:
Have a look at the Student class. Each student will do his work in a
background thread. As there are 3 students, the latch will be initiated with 3.
After completing the task, a student will reduce the latch counter by 1.
So when all the students will complete the task, the latch - on which the main thread was blocked, will become zero and it will unblock the main thread.
The C++ latches work exactly the way the Java Countdown Latch
works.
Top comments (0)