Condition_variable assignment

Application without std :: condition_variable:

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

std::mutex mutex;
std::queue<int> queue;
int counter;

void loadData()
{
    while(true)
    {
        std::unique_lock<std::mutex> lock(mutex);
        queue.push(++counter);
        lock.unlock();
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

void writeData()
{
    while(true)
    {
        std::lock_guard<std::mutex> lock(mutex);
        while(queue.size() > 0)
        {
            std::cout << queue.front() << std::endl;
            queue.pop();
        }
    }
}

int main()
{
    std::thread thread1(loadData);
    std::thread thread2(writeData);
    thread1.join();
    thread2.join();
    return 0;
}

      

Application with std :: condition_variable:

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

std::mutex mutex;
std::queue<int> queue;
std::condition_variable condition_variable;
int counter;

void loadData()
{
    while(true)
    {
        std::unique_lock<std::mutex> lock(mutex);
        queue.push(++counter);
        lock.unlock();
        condition_variable.notify_one();
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

void writeData()
{
    while(true)
    {
        std::unique_lock<std::mutex> lock(mutex);
        condition_variable.wait(lock, [](){return !queue.empty();});
        std::cout << queue.front() << std::endl;
        queue.pop();
    }
}

int main()
{
    std::thread thread1(loadData);
    std::thread thread2(writeData);
    thread1.join();
    thread2.join();
    return 0;
}

      

If I am correct it means that the second version of this application is insecure due to the queue.empty () function being used without any synchronization, so there are no locks. And my question is, should we use condition_variables if they cause problems like the ones mentioned earlier?

+3


source to share


2 answers


Your first example is busy waiting - there is a thread on the lock that checks, then unlocks the lock. This increases the conflict of mutexes and waste to a whole CPU where nothing is being processed.

In the second example, the wait thread is mostly sleeping. It only wakes up when there is data ready, or when a "false wakeup" occurs (with standard resolutions).



When it wakes up, it restarts the mutex and checks the predicate. If the predicate fails, it releases the lock and waits for the condition variable again.

This is safe because the predicate is guaranteed to run within the mutex you purchased and passed to the function wait

.

+4


source


The second code is safe as the call is wait(lock, pred)

equivalent (directly from the standard):

while (!pred())
    wait(lock);

      

And the call is wait(lock)

let go (unblock) lock

and re-set (block) it on notification.



In your case, this is equivalent to:

auto pred = [](){return !queue.empty();};
std::unique_lock<std::mutex> lock(mutex); // acquire
while (!pred) { // Ok, we are locked
    condition_variable.wait(lock); // release
    // if you get here, the lock as been re-acquired
}

      

Thus, all calls to yours pred

are made using lock

blocked / received - there is no problem here if all other operations with are queue

also protected.

0


source







All Articles