condition_variable / shared variable
-
This blocks 1/multiple thread(s) until another thread modifies a shared
variable (the condition), and notifies the condition_variable.
Advantages?
1. Avoids busy waiting, which spinlock does. For instance, if you have a thread (or multiple threads) that can't continue onward until a queue is empty, the busy waiting approach would be to just doing something.
2. Similar to [Semaphores]: CV and semaphores are similar, both signals the sleeping threads, but counting semaphore is used for n available things.
Code
Thread1 signalling Thread2
#include <stdio.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>
using namespace std;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *fun1(void* arg) {
//pthread_mutex_lock(&mutex);
cout << "Thread1 waiting on condition\n";
//pthread_cond_wait() might provide unexepected result without mutex
pthread_cond_wait(&cond, &mutex);
cout << "Condition satisfied\n";
//pthread_mutex_unlock(&mutex);
}
void *fun2(void* arg) {
sleep(1);
cout << "Thread2 signalled the condition\n";
pthread_cond_signal(&cond);
}
int main(){
pthread_t tid1,tid2;
pthread_create(&tid1, 0, fun1, 0);
pthread_create(&tid2, 0, fun2, 0);
pthread_join(tid1, 0);
pthread_join(tid2, 0);
}
$ ./a.out
Thread1 waiting on condition
Thread2 signalled the condition
Condition satisfied
Ping Pong Game
1. Using pthread
#include <stdio.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>
using namespace std;
pthread_cond_t cond_ping = PTHREAD_COND_INITIALIZER;
pthread_cond_t cond_pong = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int j=0;
void *pong(void* arg) {
while (j < 10) {
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond_pong, &mutex);
++j;
cout << "Pong" << ",j:"<< j << "\n";
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond_ping);
}
}
void *ping(void* arg) {
while( j < 10) {
sleep(1);
pthread_cond_signal(&cond_pong);
pthread_mutex_lock(&mutex);
++j;
cout << "Ping" << ",j:" << j << "\n";
pthread_cond_wait(&cond_ping, &mutex);
pthread_mutex_unlock(&mutex);
}
}
int main(){
pthread_t tid1,tid2;
pthread_create(&tid1, 0, ping, 0);
pthread_create(&tid2, 0, pong, 0);
pthread_join(tid1, 0);
pthread_join(tid2, 0);
}
$ ./a.out
Ping,j:1
Pong,j:2
Ping,j:3
Pong,j:4
Ping,j:5
Pong,j:6
Ping,j:7
Pong,j:8
Ping,j:9
Pong,j:10
2. Ping Pong Game (Using std::condition_variable, unique_lock <mutex>)
| 2 Threads | 4 Threads | Description |
|---|---|---|
|
|
condition_variable: Block the thread until certain condition
is met std::condition_variable only works with unique_lock Seqeunce: Thread-1 waiting on condition variable, thread-2 changes cond variable then thread-1 starts in critical section. Thread-2 publishing on condition_variable: - a. acquire a std::mutex (typically via std::lock_guard) - b. perform the modification while the lock is held - c. execute notify_one() or notify_all() Thread-1 waiting on condition_variable: - a. acquire a std::unique_lock<std::mutex>, same mutex as used to protect the shared variable - b. execute wait, wait_for, or wait_until. |