📢 Nuovo Corso Bootstrap Completo disponibile!

Esercizi Condition Variables C++

Ecco degli esercizi semplici con soluzione per praticare le basi delle condition variables nel threading in C++.

Esercizio 1

Utilizzare una condition variable per sincronizzare l'accesso a una risorsa condivisa tra due thread.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool pronto = false;
int dato = 0;
void produttore() {
std::unique_lock<std::mutex> lock(mtx);
dato = 42;
pronto = true;
cv.notify_one();
}
void consumatore() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return pronto; });
std::cout << "Dato ricevuto: " << dato << std::endl;
}
int main() {
std::thread t1(produttore);
std::thread t2(consumatore);
t1.join();
t2.join();
return 0;
}

Esercizio 2

Implementare una coda thread-safe utilizzando condition variables.
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
template <typename T>
class CodaThreadSafe {
private:
std::queue<T> coda;
std::mutex mtx;
std::condition_variable cv;
public:
void push(T val) {
std::unique_lock<std::mutex> lock(mtx);
coda.push(val);
cv.notify_one();
}
T pop() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this]{ return !coda.empty(); });
T val = coda.front();
coda.pop();
return val;
}
};
CodaThreadSafe<int> coda;
void produttore() {
for (int i = 0; i < 5; ++i) {
coda.push(i);
std::cout << "Prodotto: " << i << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
void consumatore() {
for (int i = 0; i < 5; ++i) {
int val = coda.pop();
std::cout << "Consumato: " << val << std::endl;
}
}
int main() {
std::thread t1(produttore);
std::thread t2(consumatore);
t1.join();
t2.join();
return 0;
}

Esercizio 3

Utilizzare una condition variable per implementare un semplice semaforo.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
class Semaforo {
private:
std::mutex mtx;
std::condition_variable cv;
int contatore;
public:
Semaforo(int valore_iniziale) : contatore(valore_iniziale) {}
void wait() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] { return contatore > 0; });
--contatore;
}
void signal() {
std::unique_lock<std::mutex> lock(mtx);
++contatore;
cv.notify_one();
}
};
Semaforo semaforo(1);
void funzione1() {
semaforo.wait();
std::cout << "Funzione 1 in esecuzione" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
semaforo.signal();
}
void funzione2() {
semaforo.wait();
std::cout << "Funzione 2 in esecuzione" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
semaforo.signal();
}
int main() {
std::thread t1(funzione1);
std::thread t2(funzione2);
t1.join();
t2.join();
return 0;
}

Esercizio 4

Utilizzare una condition variable per implementare un ciclo produttore-consumatore con più produttori e consumatori.
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
std::queue<int> coda;
std::mutex mtx;
std::condition_variable cv;
const int MAX_CODA = 10;
bool done = false;
void produttore(int id) {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return coda.size() < MAX_CODA; });
coda.push(i);
std::cout << "Produttore " << id << " ha prodotto: " << i << std::endl;
cv.notify_all();
}
}
void consumatore(int id) {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !coda.empty() || done; });
if (done && coda.empty()) break;
int val = coda.front();
coda.pop();
std::cout << "Consumatore " << id << " ha consumato: " << val << std::endl;
cv.notify_all();
}
}
int main() {
std::thread produttori[3], consumatori[3];
for (int i = 0; i < 3; ++i) {
produttori[i] = std::thread(produttore, i+1);
consumatori[i] = std::thread(consumatore, i+1);
}
for (int i = 0; i < 3; ++i) {
produttori[i].join();
}
{
std::unique_lock<std::mutex> lock(mtx);
done = true;
cv.notify_all();
}
for (int i = 0; i < 3; ++i) {
consumatori[i].join();
}
return 0;
}

Esercizio 5

Utilizzare una condition variable per implementare un sistema di lettori-scrittori.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
int lettori = 0;
bool scrittura_in_corso = false;
void lettore(int id) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !scrittura_in_corso; });
++lettori;
lock.unlock();
std::cout << "Lettore " << id << " sta leggendo." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
lock.lock();
--lettori;
if (lettori == 0) {
cv.notify_all();
}
lock.unlock();
}
void scrittore(int id) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !scrittura_in_corso && lettori == 0; });
scrittura_in_corso = true;
lock.unlock();
std::cout << "Scrittore " << id << " sta scrivendo." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
lock.lock();
scrittura_in_corso = false;
cv.notify_all();
lock.unlock();
}
int main() {
std::thread lettori[3], scrittori[2];
for (int i = 0; i < 3; ++i) {
lettori[i] = std::thread(lettore, i+1);
}
for (int i = 0; i < 2; ++i) {
scrittori[i] = std::thread(scrittore, i+1);
}
for (int i = 0; i < 3; ++i) {
lettori[i].join();
}
for (int i = 0; i < 2; ++i) {
scrittori[i].join();
}
return 0;
}
``
`
</div>
</details>
### Esercizio 6
<details class="mb-10">
<summary class="cursor-pointer">Utilizzare una condition variable per sincronizzare l'accesso a un contatore condiviso tra più thread.</summary>
<div class="py-5">
```cpp
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
int contatore = 0;
std::mutex mtx;
std::condition_variable cv;
void incrementa(int id) {
for (int i = 0; i < 5; ++i) {
std::unique_lock<std::mutex> lock(mtx);
++contatore;
std::cout << "Thread " << id << " incrementa a " << contatore << std::endl;
cv.notify_all();
cv.wait(lock, []{ return contatore % 5 == 0; });
}
}
void stampa() {
while (true) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return contatore % 5 == 0 && contatore > 0; });
std::cout << "Contatore raggiunge multiplo di 5: " << contatore << std::endl;
if (contatore >= 20) break;
}
}
int main() {
std::thread t1(incrementa, 1);
std::thread t2(incrementa, 2);
std::thread t3(stampa);
t1.join();
t2.join();
t3.join();
return 0;
}

Esercizio 7

Utilizzare una condition variable per implementare una barriera di sincronizzazione.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
const int NUM_THREAD = 5;
int count = 0;
std::mutex mtx;
std::condition_variable cv;
void threadFunzione(int id) {
std::cout << "Thread " << id << " pronto\n";
std::unique_lock<std::mutex> lock(mtx);
++count;
if (count == NUM_THREAD) {
cv.notify_all();
} else {
cv.wait(lock, []{ return count == NUM_THREAD; });
}
std::cout << "Thread " << id << " inizia\n";
}
int main() {
std::thread threads[NUM_THREAD];
for (int i = 0; i < NUM_THREAD; ++i) {
threads[i] = std::thread(threadFunzione, i + 1);
}
for (int i = 0; i < NUM_THREAD; ++i) {
threads[i].join();
}
return 0;
}

Esercizio 8

Utilizzare una condition variable per implementare un sistema di lettura e scrittura con priorità ai lettori.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
int lettori = 0;
bool scrittura_in_corso = false;
void lettore(int id) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !scrittura_in_corso; });
++lettori;
lock.unlock();
std::cout << "Lettore " << id << " sta leggendo.\n";
std::this_thread::sleep_for(std::chrono::milliseconds(500));
lock.lock();
--lettori;
if (lettori == 0) {
cv.notify_all();
}
lock.unlock();
}
void scrittore(int id) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return !scrittura_in_corso && lettori == 0; });
scrittura_in_corso = true;
lock.unlock();
std::cout << "Scrittore " << id << " sta scrivendo.\n";
std::this_thread::sleep_for(std::chrono::seconds(1));
lock.lock();
scrittura_in_corso = false;
cv.notify_all();
lock.unlock();
}
int main() {
std::thread lettori[3], scrittori[2];
for (int i = 0; i < 3; ++i) {
lettori[i] = std::thread(lettore, i+1);
}
for (int i = 0; i < 2; ++i) {
scrittori[i] = std::thread(scrittore, i+1);
}
for (int i = 0; i < 3; ++i) {
lettori[i].join();
}
for (int i = 0; i < 2; ++i) {
scrittori[i].join();
}
return 0;
}