C++ 20 - <semaphore> Header
Last Updated :
22 Sep, 2023
The C++20 <semaphore> header is part of the Concurrency Library Technical Specification (TS). Semaphores are synchronization primitives that help control access to shared resources in multi-threaded programs. The <semaphore> header provides the standard C++ way to work with semaphores.
In this article, we have covered important sections of semaphore headers such as the main classes, and usage of semaphore headers in C++20 along with examples.
How to use <semaphore> in C++20?
Below is the step-by-step tutorial on the use of semaphores in C++ programs.
STEP 1: Include the Header
To use semaphores in your C++ program, you need to include the <semaphore> header:
#include <semaphore>
STEP 2: Semaphore Basics
You can create a semaphore object like this:
std::counting_semaphore<size_t> sem(1); // Initialize a semaphore with an initial count of 1
std::counting_semaphore is a type of semaphore that allows a specified number of threads to access a resource concurrently. In this example, only one thread can access the resource protected by sem at a time.
STEP 3: Acquiring and Releasing
There are 3 methods to acquire and release a semaphore object:
Method 1: Aquire and Release
To acquire (lock) the semaphore, you can use the acquire method:
sem.acquire();
// Critical section code
sem.release();
The acquire method decreases the semaphore count by one, effectively locking it. The release method increases the count, releasing the semaphore.
Method 2: Try-Aquire
You can also use the try_acquire method to try to acquire the semaphore without blocking:
if (sem.try_acquire()) {
// Successfully acquired the semaphore
// Critical section code
sem.release();
} else {
// Semaphore was not acquired
}
Method 2: Waiting with Timeout
C++20 also introduced the try_acquire_for and try_acquire_until methods to try acquiring the semaphore with a timeout.
if (sem.try_acquire_for(std::chrono::seconds(1))) {
// Successfully acquired the semaphore within 1 second
// Critical section code
sem.release();
} else {
// Semaphore was not acquired within 1 second
}
Types of Semaphores
The <semaphores> header provides two types of semaphores that are:
1. std::counting_semaphore
A counting semaphore is a synchronization primitive that allows multiple threads to access a shared resource up to a certain limit.
- It is a generalization of a mutex or a binary semaphore.
- You can initialize a counting semaphore with an initial count, which represents the number of threads that can access the resource simultaneously without blocking.
- Threads can acquire and release counts, and the semaphore's count is incremented or decremented accordingly.
- If a thread tries to acquire more counts than are available, it will block until counts become available.
Example
C++
// C++ Program to illustrate the use of counting_semaphore
#include <iostream>
#include <semaphore>
#include <thread>
using namespace std;
// Initialize semaphore with a count of 3
counting_semaphore<10> semaphore(3);
void worker(int id)
{
// aquiring
semaphore.acquire();
// doing some work
cout << "Thread " << id << " acquired the semaphore."
<< endl;
// releasing
semaphore.release();
cout << "Thread " << id << " released the semaphore."
<< endl;
}
// driver code
int main()
{
thread t1(worker, 1);
thread t2(worker, 2);
thread t3(worker, 3);
t1.join();
t2.join();
t3.join();
return 0;
}
Output
Thread 2 acquired the semaphore.
Thread 2 released the semaphore.
Thread 1 acquired the semaphore.
Thread 1 released the semaphore.
Thread 3 acquired the semaphore.
Thread 3 released the semaphore.
2. std::binary_semaphore
A binary semaphore is a simpler version of a semaphore that can have only two values: 0 and 1.
- It is often used for basic mutual exclusion or signaling between two threads.
- It can be thought of as a mutex with a more lightweight interface.
Example
C++
// C++ program to illustrate the binary semaphores
#include <iostream>
#include <semaphore>
#include <thread>
using namespace std;
// Initialize with a count of 1 (binary)
binary_semaphore semaphore(1);
void worker(int id)
{
// aquire semaphore
semaphore.acquire();
cout << "Thread " << id << " acquired the semaphore."
<< endl;
// Do some work
semaphore.release();
// release
cout << "Thread " << id << " released the semaphore."
<< endl;
}
// driver code
int main()
{
thread t1(worker, 1);
thread t2(worker, 2);
t1.join();
t2.join();
return 0;
}
Output
Thread 1 acquired the semaphore.
Thread 1 released the semaphore.
Thread 2 acquired the semaphore.
Thread 2 released the semaphore.
Advantages of Semaphores
The following are some main advantages of using semaphores over similar constructs:
- Fine-Grained Control: To access a resource concurrently, semaphores can be configured to allow a specific number of threads.
- Generalization: Semaphores are more versatile and can be used to implement other synchronization primitives.
- Multiple Resources: Counting semaphores can be used to manage multiple instances of a resource. This makes them suitable for scenarios where you need to control access to a pool of resources (e.g., a thread pool or a connection pool).
- Blocking and Waiting: Blocking and waiting mechanisms in semaphores allow threads to wait until a resource becomes available again.
- Timeouts: A timeout could be specified when acquiring a semaphore, which makes it more useful.
Similar Reads
C++ 20 - <numbers> Header C++20 has a recently developed header file labeled that incorporates mathematical functions and constants. Its purpose is to provide standard library support for mathematical operations, simplifying the process for C++ programmers to incorporate mathematical functions and constants into their progra
3 min read
C++23 <print> Header C++ has long been a powerful language, known for its versatility and performance. With each new standard release, the language evolves, introducing features that enhance developer productivity and code readability. One of these additions of the C++ 23 standard is the introduction of <print> he
3 min read
C++ 23 <stacktrace> - Header Imagine you're on an adventure, exploring a vast and dense forest. Along the way, you encounter challenges that might leave you feeling lost or confused. In such situations, a map or compass would be invaluable tools to guide you back on track. Similarly, in the world of programming, stack traces se
5 min read
< iomanip > Header in C++ In C++, the <iomanip> header file defines the manipulator functions that are used to manipulate the format of the input and output of our program. It is a part of the input/output library in C++. In this article, we will discuss the functions and facilities provided in the <iomanip> head
9 min read
C++ 11 - <atomic> Header In C++11, the <atomic> header introduces a powerful mechanism for managing concurrent access to shared data in multithreaded applications. This header provides atomic types and operations that ensure safe access to variables, preventing data races and potential issues in multithreaded code. In
4 min read