Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/thread/condition variable"

From cppreference.com
< cpp‎ | thread
(c -> c/core.)
 
(30 intermediate revisions by 19 users not shown)
Line 1: Line 1:
 
{{cpp/title|condition_variable}}
 
{{cpp/title|condition_variable}}
 
{{cpp/thread/condition_variable/navbar}}
 
{{cpp/thread/condition_variable/navbar}}
{{ddcl | header=condition_variable | since=c++11 | 1=
+
{{ddcl|header=condition_variable|since=c++11|1=
 
class condition_variable;
 
class condition_variable;
 
}}
 
}}
  
The {{tt|condition_variable}} class is a synchronization primitive that can be used to block a thread, or multiple threads at the same time, until another thread both modifies a shared variable (the ''condition''), and notifies the {{tt|condition_variable}}.
+
{{tt|std::condition_variable}} is a synchronization primitive used with a {{lc|std::mutex}} to block one or more threads until another thread both modifies a shared variable (the ''condition'') and notifies the {{tt|std::condition_variable}}.
  
The thread that intends to modify the variable has to
+
The thread that intends to modify the shared variable must:
# acquire a {{tt|std::mutex}} (typically via {{lc|std::lock_guard}})
+
# Acquire a {{lc|std::mutex}} (typically via {{lc|std::lock_guard}}).
# perform the modification while the lock is held
+
# Modify the shared variable while the lock is owned.
# execute {{lc|notify_one}} or {{lc|notify_all}} on the {{tt|std::condition_variable}} (the lock does not need to be held for notification)
+
# Call {{lc|notify_one}} or {{lc|notify_all}} on the {{tt|std::condition_variable}} (can be done after releasing the lock).
Even if the shared variable is atomic, it must be modified under the mutex in order to correctly publish the modification to the waiting thread.
+
Even if the shared variable is atomic, it must be modified while owning the mutex to [https://stackoverflow.com/questions/38147825/ correctly] publish the modification to the waiting thread.
  
Any thread that intends to wait on {{tt|std::condition_variable}} has to
+
Any thread that intends to wait on a {{tt|std::condition_variable}} must:
# acquire a {{c|std::unique_lock<std::mutex>}}, on the same mutex as used to protect the shared variable
+
# Acquire a {{c/core|std::unique_lock<std::mutex>}} on the mutex used to protect the shared variable.
# execute {{lc|wait}}, {{lc|wait_for}}, or {{lc|wait_until}}. The wait operations atomically release the mutex and suspend the execution of the thread.
+
# Do one of the following:
# When the condition variable is notified, a timeout expires, or a [[enwiki:Spurious_wakeup|spurious wakeup]] occurs, the thread is awakened, and the mutex is atomically reacquired. The thread should then check the condition and resume waiting if the wake up was spurious.
+
:# Check the condition, in case it was already updated and notified.
 +
:# Call {{lc|wait}}, {{lc|wait_for}}, or {{lc|wait_until}} on the {{tt|std::condition_variable}} (atomically releases the mutex and suspends thread execution until the condition variable is notified, a timeout expires, or a {{enwiki|Spurious wakeup|spurious wakeup}} occurs, then atomically acquires the mutex before returning).
 +
:# Check the condition and resume waiting if not satisfied.
 +
:: or:
 +
:# Use the predicated overload of {{lc|wait}}, {{lc|wait_for}}, and {{lc|wait_until}}, which performs the same three steps.
  
{{tt|std::condition_variable}} works only with {{c|std::unique_lock<std::mutex>}}; this restriction allows for maximal efficiency on some platforms. {{lc|std::condition_variable_any}} provides a condition variable that works with any {{named req|BasicLockable}} object, such as {{lc|std::shared_lock}}.
+
{{tt|std::condition_variable}} works only with {{c/core|std::unique_lock<std::mutex>}}, which allows for maximal efficiency on some platforms. {{lc|std::condition_variable_any}} provides a condition variable that works with any {{named req|BasicLockable}} object, such as {{lc|std::shared_lock}}.
  
 
Condition variables permit concurrent invocation of the {{lc|wait}}, {{lc|wait_for}}, {{lc|wait_until}}, {{lc|notify_one}} and {{lc|notify_all}} member functions.
 
Condition variables permit concurrent invocation of the {{lc|wait}}, {{lc|wait_for}}, {{lc|wait_until}}, {{lc|notify_one}} and {{lc|notify_all}} member functions.
Line 24: Line 28:
 
The class {{tt|std::condition_variable}} is a {{named req|StandardLayoutType}}. It is not {{named req|CopyConstructible}}, {{named req|MoveConstructible}}, {{named req|CopyAssignable}}, or {{named req|MoveAssignable}}.
 
The class {{tt|std::condition_variable}} is a {{named req|StandardLayoutType}}. It is not {{named req|CopyConstructible}}, {{named req|MoveConstructible}}, {{named req|CopyAssignable}}, or {{named req|MoveAssignable}}.
  
===Member types===
+
===Nested types===
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc hitem | Member type | Definition}}
+
{{dsc hitem|Name|Definition}}
{{dsc | {{tt|native_handle_type}} | ''implementation-defined''}}
+
{{dsc|{{tt|native_handle_type}}|implementation-defined}}
 
{{dsc end}}
 
{{dsc end}}
  
 
===Member functions===
 
===Member functions===
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc inc | cpp/thread/condition_variable/dsc constructor | condition_variable}}
+
{{dsc inc|cpp/thread/condition_variable/dsc constructor|condition_variable}}
{{dsc inc | cpp/thread/condition_variable/dsc destructor | condition_variable}}
+
{{dsc inc|cpp/thread/condition_variable/dsc destructor|condition_variable}}
{{dsc inc | cpp/thread/condition_variable/dsc operator{{=}} | condition_variable}}
+
{{dsc inc|cpp/thread/condition_variable/dsc operator{{=}}|condition_variable}}
  
{{dsc h2 | Notification}}
+
{{dsc h2|Notification}}
{{dsc inc | cpp/thread/condition_variable/dsc notify_one | condition_variable}}
+
{{dsc inc|cpp/thread/condition_variable/dsc notify_one|condition_variable}}
{{dsc inc | cpp/thread/condition_variable/dsc notify_all | condition_variable}}
+
{{dsc inc|cpp/thread/condition_variable/dsc notify_all|condition_variable}}
  
{{dsc h2 | Waiting}}
+
{{dsc h2|Waiting}}
{{dsc inc | cpp/thread/condition_variable/dsc wait | condition_variable}}
+
{{dsc inc|cpp/thread/condition_variable/dsc wait|condition_variable}}
{{dsc inc | cpp/thread/condition_variable/dsc wait_for | condition_variable}}
+
{{dsc inc|cpp/thread/condition_variable/dsc wait_for|condition_variable}}
{{dsc inc | cpp/thread/condition_variable/dsc wait_until | condition_variable}}
+
{{dsc inc|cpp/thread/condition_variable/dsc wait_until|condition_variable}}
  
{{dsc h2 | Native handle}}
+
{{dsc h2|Native handle}}
{{dsc inc | cpp/thread/condition_variable/dsc native handle | condition_variable}}
+
{{dsc inc|cpp/thread/condition_variable/dsc native handle|condition_variable}}
 
{{dsc end}}
 
{{dsc end}}
  
 
===Example===
 
===Example===
{{example| {{tt|condition_variable}} is used in combination with a {{lc|std::mutex}} to facilitate inter-thread communication.
+
{{example
 +
|{{tt|std::condition_variable}} is used in combination with a {{lc|std::mutex}} to facilitate inter-thread communication.
 
|code=
 
|code=
 +
#include <condition_variable>
 
#include <iostream>
 
#include <iostream>
 +
#include <mutex>
 
#include <string>
 
#include <string>
 
#include <thread>
 
#include <thread>
#include <mutex>
 
#include <condition_variable>
 
  
 
std::mutex m;
 
std::mutex m;
Line 66: Line 71:
 
void worker_thread()
 
void worker_thread()
 
{
 
{
     // Wait until main() sends data
+
     // wait until main() sends data
     std::unique_lock<std::mutex> lk(m);
+
     std::unique_lock lk(m);
     cv.wait(lk, []{return ready;});
+
     cv.wait(lk, []{ return ready; });
 
+
   
     // after the wait, we own the lock.
+
     // after the wait, we own the lock
 
     std::cout << "Worker thread is processing data\n";
 
     std::cout << "Worker thread is processing data\n";
 
     data += " after processing";
 
     data += " after processing";
 
+
   
     // Send data back to main()
+
     // send data back to main()
 
     processed = true;
 
     processed = true;
 
     std::cout << "Worker thread signals data processing completed\n";
 
     std::cout << "Worker thread signals data processing completed\n";
 
      
 
      
     // Manual unlocking is done before notifying, to avoid waking up
+
     // manual unlocking is done before notifying, to avoid waking up
 
     // the waiting thread only to block again (see notify_one for details)
 
     // the waiting thread only to block again (see notify_one for details)
 
     lk.unlock();
 
     lk.unlock();
Line 87: Line 92:
 
{
 
{
 
     std::thread worker(worker_thread);
 
     std::thread worker(worker_thread);
 
+
   
 
     data = "Example data";
 
     data = "Example data";
 
     // send data to the worker thread
 
     // send data to the worker thread
 
     {
 
     {
         std::lock_guard<std::mutex> lk(m);
+
         std::lock_guard lk(m);
 
         ready = true;
 
         ready = true;
 
         std::cout << "main() signals data ready for processing\n";
 
         std::cout << "main() signals data ready for processing\n";
 
     }
 
     }
 
     cv.notify_one();
 
     cv.notify_one();
 
+
   
 
     // wait for the worker
 
     // wait for the worker
 
     {
 
     {
         std::unique_lock<std::mutex> lk(m);
+
         std::unique_lock lk(m);
         cv.wait(lk, []{return processed;});
+
         cv.wait(lk, []{ return processed; });
 
     }
 
     }
 
     std::cout << "Back in main(), data = " << data << '\n';
 
     std::cout << "Back in main(), data = " << data << '\n';
 
+
   
 
     worker.join();
 
     worker.join();
 
}
 
}
| output=
+
|output=
 
main() signals data ready for processing
 
main() signals data ready for processing
 
Worker thread is processing data
 
Worker thread is processing data
Line 113: Line 118:
 
}}
 
}}
  
{{example| syncronisation primitives used to send data from one thread to another grouped in a class
+
===See also===
|code =  
+
{{dsc begin}}
#include <condition_variable>
+
{{dsc inc|cpp/thread/dsc condition_variable_any}}
#include <mutex>
+
{{dsc inc|cpp/thread/dsc mutex}}
#include <thread>
+
{{dsc inc|cpp/thread/dsc lock_guard}}
#include <iostream>
+
{{dsc inc|cpp/thread/dsc unique_lock}}
#include <queue>
+
{{dsc end}}
#include <chrono>
+
 
+
class condvarQueue
+
{
+
    std::queue<int> produced_nums;
+
    std::mutex m;
+
    std::condition_variable cond_var;
+
    bool done = false;
+
    bool notified = false;
+
public:
+
    void push(int i)
+
    {
+
        std::unique_lock<std::mutex> lock(m);
+
        produced_nums.push(i);
+
        notified = true;
+
        cond_var.notify_one();
+
    }
+
 
+
    template<typename Consumer>
+
    void consume(Consumer consumer)
+
    {
+
        std::unique_lock<std::mutex> lock(m);
+
        while (!done) {
+
            while (!notified) {  // loop to avoid spurious wakeups
+
                cond_var.wait(lock);
+
            }  
+
            while (!produced_nums.empty()) {
+
                consumer(produced_nums.front());
+
                produced_nums.pop();
+
            }  
+
            notified = false;
+
        }  
+
    }
+
 
+
    void close()
+
    {
+
        done = true;
+
        notified = true;
+
        cond_var.notify_one();
+
    }
+
};
+
 
+
int main()
+
{
+
    condvarQueue queue;
+
+
    std::thread producer([&]() {
+
        for (int i = 0; i < 5; ++i) {
+
            std::this_thread::sleep_for(std::chrono::seconds(1));
+
            std::cout << "producing " << i << '\n';
+
            queue.push(i);
+
        }  
+
        queue.close();
+
    });
+
+
    std::thread consumer([&]() {
+
        queue.consume([](int input){
+
            std::cout << "consuming " << input << '\n';
+
        });
+
    });
+
 
+
    producer.join();
+
    consumer.join();
+
}
+
| output =
+
producing 0
+
consuming 0
+
producing 1
+
consuming 1
+
producing 2
+
consuming 2
+
producing 3
+
consuming 3
+
producing 4
+
consuming 4
+
}}
+
  
[[de:cpp/thread/condition variable]]
+
{{langlinks|de|es|fr|it|ja|pt|ru|zh}}
[[es:cpp/thread/condition variable]]
+
[[fr:cpp/thread/condition variable]]
+
[[it:cpp/thread/condition variable]]
+
[[ja:cpp/thread/condition variable]]
+
[[pt:cpp/thread/condition variable]]
+
[[ru:cpp/thread/condition variable]]
+
[[zh:cpp/thread/condition variable]]
+

Latest revision as of 22:17, 4 March 2024

 
 
Concurrency support library
Threads
(C++11)
(C++20)
this_thread namespace
(C++11)
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
(C++11)
Generic lock management
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Condition variables
condition_variable
(C++11)
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
(C++11)
Safe Reclamation
(C++26)
Hazard Pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11)(deprecated in C++20)
(C++11)(deprecated in C++20)
Memory ordering
Free functions for atomic operations
Free functions for atomic flags
 
 
Defined in header <condition_variable>
class condition_variable;
(since C++11)

std::condition_variable is a synchronization primitive used with a std::mutex to block one or more threads until another thread both modifies a shared variable (the condition) and notifies the std::condition_variable.

The thread that intends to modify the shared variable must:

  1. Acquire a std::mutex (typically via std::lock_guard).
  2. Modify the shared variable while the lock is owned.
  3. Call notify_one or notify_all on the std::condition_variable (can be done after releasing the lock).

Even if the shared variable is atomic, it must be modified while owning the mutex to correctly publish the modification to the waiting thread.

Any thread that intends to wait on a std::condition_variable must:

  1. Acquire a std::unique_lock<std::mutex> on the mutex used to protect the shared variable.
  2. Do one of the following:
  1. Check the condition, in case it was already updated and notified.
  2. Call wait, wait_for, or wait_until on the std::condition_variable (atomically releases the mutex and suspends thread execution until the condition variable is notified, a timeout expires, or a spurious wakeup occurs, then atomically acquires the mutex before returning).
  3. Check the condition and resume waiting if not satisfied.
or:
  1. Use the predicated overload of wait, wait_for, and wait_until, which performs the same three steps.

std::condition_variable works only with std::unique_lock<std::mutex>, which allows for maximal efficiency on some platforms. std::condition_variable_any provides a condition variable that works with any BasicLockable object, such as std::shared_lock.

Condition variables permit concurrent invocation of the wait, wait_for, wait_until, notify_one and notify_all member functions.

The class std::condition_variable is a StandardLayoutType. It is not CopyConstructible, MoveConstructible, CopyAssignable, or MoveAssignable.

Contents

[edit] Nested types

Name Definition
native_handle_type implementation-defined

[edit] Member functions

constructs the object
(public member function) [edit]
destructs the object
(public member function) [edit]
operator=
[deleted]
not copy-assignable
(public member function) [edit]
Notification
notifies one waiting thread
(public member function) [edit]
notifies all waiting threads
(public member function) [edit]
Waiting
blocks the current thread until the condition variable is awakened
(public member function) [edit]
blocks the current thread until the condition variable is awakened or after the specified timeout duration
(public member function) [edit]
blocks the current thread until the condition variable is awakened or until specified time point has been reached
(public member function) [edit]
Native handle
returns the native handle
(public member function) [edit]

[edit] Example

std::condition_variable is used in combination with a std::mutex to facilitate inter-thread communication.

#include <condition_variable>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
 
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
 
void worker_thread()
{
    // wait until main() sends data
    std::unique_lock lk(m);
    cv.wait(lk, []{ return ready; });
 
    // after the wait, we own the lock
    std::cout << "Worker thread is processing data\n";
    data += " after processing";
 
    // send data back to main()
    processed = true;
    std::cout << "Worker thread signals data processing completed\n";
 
    // manual unlocking is done before notifying, to avoid waking up
    // the waiting thread only to block again (see notify_one for details)
    lk.unlock();
    cv.notify_one();
}
 
int main()
{
    std::thread worker(worker_thread);
 
    data = "Example data";
    // send data to the worker thread
    {
        std::lock_guard lk(m);
        ready = true;
        std::cout << "main() signals data ready for processing\n";
    }
    cv.notify_one();
 
    // wait for the worker
    {
        std::unique_lock lk(m);
        cv.wait(lk, []{ return processed; });
    }
    std::cout << "Back in main(), data = " << data << '\n';
 
    worker.join();
}

Output:

main() signals data ready for processing
Worker thread is processing data
Worker thread signals data processing completed
Back in main(), data = Example data after processing

[edit] See also

provides a condition variable associated with any lock type
(class) [edit]
(C++11)
provides basic mutual exclusion facility
(class) [edit]
implements a strictly scope-based mutex ownership wrapper
(class template) [edit]
implements movable mutex ownership wrapper
(class template) [edit]