Communicating between threads using `std::jthread`

Shared Variables

Shared variables are a simple way to communicate between threads by allowing them to access and modify the same memory locations. However, using shared variables requires careful synchronization to avoid race conditions. Here’s an example:

#include <iostream>
#include <thread>

int sharedData = 0;
std::mutex mutex;

void workerThread()
{
    for (int i = 0; i < 10; ++i)
    {
        std::lock_guard<std::mutex> lock(mutex);
        sharedData += i;
    }
}

int main()
{
    std::jthread thread(workerThread);
    
    // Access the shared variable
    std::lock_guard<std::mutex> lock(mutex);
    std::cout << "Shared data: " << sharedData << std::endl;
    
    return 0;
}

In this example, we create a shared variable sharedData and a std::mutex for synchronization. The workerThread function is executed concurrently in a separate thread and increments the sharedData variable. In the main function, we lock the mutex to safely access the shared variable and print its value.

Message Passing

Message passing involves sending data or messages between threads through a communication channel. The channel can be a thread-safe queue, a mailbox, or any other inter-thread communication mechanism. Here’s an example using std::queue as the message passing channel:

#include <iostream>
#include <queue>
#include <thread>
#include <condition_variable>

std::queue<int> messageQueue;
std::mutex mutex;
std::condition_variable cv;

void producerThread()
{
    // Produce some messages
    for (int i = 0; i < 10; ++i)
    {
        std::lock_guard<std::mutex> lock(mutex);
        messageQueue.push(i);
        cv.notify_one();
    }
}

void consumerThread()
{
    // Consume messages
    while (true)
    {
        std::unique_lock<std::mutex> lock(mutex);
        cv.wait(lock, [] { return !messageQueue.empty(); });
        
        int message = messageQueue.front();
        messageQueue.pop();
        lock.unlock();
        
        // Process the message
        std::cout << "Received message: " << message << std::endl;
        
        if (message == 9)
            break;
    }
}

int main()
{
    std::jthread producer(producerThread);
    std::jthread consumer(consumerThread);
    
    return 0;
}

In this example, we have two threads: producerThread and consumerThread. The producer thread generates messages and pushes them into the messageQueue, while the consumer thread continuously waits for messages to be available. Once a message is received, it is processed accordingly. The condition variable cv is used to synchronize the communication between the two threads.

Conclusion

In this blog post, we explored two techniques for communicating between threads using std::jthread in C++. Shared variables can be used, but they require proper synchronization to avoid race conditions. Message passing, on the other hand, provides a more robust and flexible way of thread communication. Depending on your requirements, you can choose the appropriate technique to ensure effective communication and synchronization between threads.

#Threads #Concurrency