wait and notify in C/C++ shared memory

33,823

Solution 1

Instead of the Java object that you would use to wait/notify, you need two objects: a mutex and a condition variable. These are initialized with pthread_mutex_init and pthread_cond_init.

Where you would have synchronized on the Java object, use pthread_mutex_lock and pthread_mutex_unlock (note that in C you have to pair these yourself manually). If you don't need to wait/notify, just lock/unlock, then you don't need the condition variable, just the mutex. Bear in mind that mutexes are not necessarily "recursive", This means that if you're already holding the lock, you can't take it again unless you set the init flag to say you want that behaviour.

Where you would have called java.lang.Object.wait, call pthread_cond_wait or pthread_cond_timedwait.

Where you would have called java.lang.Object.notify, call pthread_cond_signal.

Where you would have called java.lang.Object.notifyAll, call pthread_cond_broadcast.

As in Java, spurious wakeups are possible from the wait functions, so you need some condition which is set before the call to signal, and checked after the call to wait, and you need to call pthread_cond_wait in a loop. As in Java, the mutex is released while you're waiting.

Unlike Java, where you can't call notify unless you hold the monitor, you can actually call pthread_cond_signal without holding the mutex. It normally doesn't gain you anything, though, and is often a really bad idea (because normally you want to lock - set condition - signal - unlock). So it's best just to ignore it and treat it like Java.

There's not really much more to it, the basic pattern is the same as Java, and not by coincidence. Do read the documentation for all those functions, though, because there are various flags and funny behaviours that you want to know about and/or avoid.

In C++ you can do a bit better than just using the pthreads API. You should at least apply RAII to the mutex lock/unlock, but depending what C++ libraries you can use, you might be better off using a more C++-ish wrapper for the pthreads functions.

Solution 2

In your title, you blend C and C++ together so casually into "C/C++". I hope, you are not writing a program that is a mixture of the two.

If you are using C++11, you will find a portable alternative to pthreads (on POSIX systems, it usually uses pthreads under the hood though).

You can use std::condition_variable + std::mutex for wait/notify. This example shows how:

#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
 
std::mutex m;
std::condition_variable cv;
std::string data;
bool mainReady = false;
bool workerReader = false;
 
void worker_thread()
{
    // Wait until main() sends data
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return mainReady;});
    }
 
    std::cout << "Worker thread is processing data: " << data << std::endl;
    data += " after processing";
 
    // Send data back to main()
    {
        std::lock_guard<std::mutex> lk(m);
        workerReady = true;
        std::cout << "Worker thread signals data processing completed\n";
    }
    cv.notify_one();
}
 
int main()
{
    std::thread worker(worker_thread);
 
    data = "Example data";
    // send data to the worker thread
    {
        std::lock_guard<std::mutex> lk(m);
        mainReady = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();
 
    // wait for the worker
    {
        std::unique_lock<std::mutex> lk(m);
        cv.wait(lk, []{return workerReady;});
    }
    std::cout << "Back in main(), data = " << data << '\n';
 

    // wait until worker dies finishes execution
    worker.join();
}

Solution 3

pthread_cond_wait and pthread_cond_signal can be used to synchronize based on a condition

Solution 4

Using Condition Variables is one way to do it: those are available when using the pthread library under Linux (see link).

A condition variable is a variable of type pthread_cond_t and is used with the appropriate functions for waiting and later, process continuation.

Solution 5

If you do not care about portability, Linux offers eventfd, which gives you exactly what you want. Each eventfd keeps an internal counter. In the default mode, reading from the eventfd blocks if the counter is zero, otherwise returns immediately. Writing to it will add to the internal counter.

The wait call would thus just be a uint64_t buf_a; read(event_fd, &buf_a, sizeof(buf_a));, where buf must be an 8-byte buffer. To notify the waiting thread, you would do uint64_t buf_b = 1; write(event_fd, &buf_b, sizeof(buf_b));.

Share:
33,823
Sajad Bahmani
Author by

Sajad Bahmani

Favorite Languages : Java , Scala , Bash , C/C++ , Python Favorite IDE : IntelliJ IDEA , Netbeans Favorite Editor : VSCode , Vim

Updated on July 13, 2022

Comments

  • Sajad Bahmani
    Sajad Bahmani almost 2 years

    How to wait and notify like in Java In C/C++ for shared memory between two or more thread?I use pthread library.

  • Michel Feinstein
    Michel Feinstein over 4 years
    "I hope, you are not writing a program that is a mixture of the two" what's the problem with mixing them both?
  • Domi
    Domi over 4 years
    @mFeinstein In practice, you mix them quite often. However, when in doubt, when thinking "Oh... Should I use a raw pointer or a smart pointer?", you are already using C++ (because C does not have smart pointers), so you definitely want to use smart pointers, unless there is some API or other restrictions that forbid the use thereof or they are clearly unnecessary etc... If you don't make that decision automatically, you distract yourself, trying to make too many unnecessary decisions, wasting time and cognitive resources you can spend on cracking harder problems.