Progress Bar
Bir fetcher thread ve bir progress bar thread’i arasındaki sekronizasyonu sağlamak için mutex ve flaglar(data_ready ve complete) kullandık.
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex mut;
string message;
bool data_ready = false;
bool complete = false;
void fetcher() {
for (int i = 0; i < 10; i++)
{
this_thread::sleep_for(chrono::milliseconds(400)); // Simulate work
lock_guard<mutex> lock(mut);
message += "...";
data_ready = true; // Indicate that data is ready
}
lock_guard<mutex> lock(mut);
complete = true;
}
void progress_bar() {
unique_lock<mutex> lock(mut,defer_lock);
while (!complete) {
lock.lock(); // Lock the mutex
if (data_ready) {
system("cls");
cout << message << endl; // Print the message
data_ready = false; // Reset the flag
}
lock.unlock(); // Unlock the mutex
this_thread::sleep_for(chrono::milliseconds(100)); // Simulate work
}
}
int main() {
thread t1(fetcher);
thread t2(progress_bar);
t1.join(); // Wait for the thread to finish
t2.join(); // Wait for the progress bar thread to finish
}Threadlerin yaptıklarını özetlemek gerekirse:
Fetcher thread
- Veriyi işle(yük)
- lock
- datayı yaz
- data_ready flag’ını değiştir
Progres bar thread
- Lock
- data_ready de değişiklik var mı kontrol et
- varsa progress barı güncelle
- Unlock
- Bir süre uyu
İşler bittince completed flag ile programdan çıkış yapıyoruz.
Gördüğünüz üzere bu yaklaşım bu işi yapmak için en iyi yaklaşım değil. Çünkü bir çok yerde lock unlock yapıyoruz. Biraz iç içe geçmiş bir durum söz konusu.
condition_variable
- Temel amacı, bir iş parçacığının (thread) belirli bir koşul sağlanana kadar beklemesini ve başka bir iş parçacığının bu koşul sağlandığında onu uyandırmasını sağlamaktır.
- Mutex ile çalışır
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
mutex mut;
condition_variable cv;
string str{};
bool completed_flag = false;
void producer() {
for (int i = 0; i < 10; ++i) {
this_thread::sleep_for(chrono::milliseconds(100)); // Simulate work
lock_guard<mutex> lock(mut);
str += "work_" + to_string(i) + "\n";
cv.notify_one();
}
lock_guard<mutex> lock(mut);
completed_flag = true;
cv.notify_one();
cout << "producer finished\n";
}
void consumer() {
while (true) {
unique_lock<mutex> lock(mut);
cv.wait(lock);
if (completed_flag) break;
cout << "hello kitty\n";
}
cout << "consumer finished\n";
}
int main() {
thread t1(producer);
thread t2(consumer);
t1.join();
t2.join();
}- notify_one : bekleyen thread’lerden birini çalıştırırken
- notify_all : tüm bekleyen thread’leri çalıştırır
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
mutex mut;
condition_variable cv;
string str{};
bool completed_flag = false;
void producer() {
for (int i = 0; i < 10; ++i) {
this_thread::sleep_for(chrono::milliseconds(100)); // Simulate work
lock_guard<mutex> lock(mut);
str += "work_" + to_string(i) + "\n";
cv.notify_one();
}
lock_guard<mutex> lock(mut);
completed_flag = true;
cv.notify_all();
cout << "producer finished\n";
}
void consumer() {
while (true) {
unique_lock<mutex> lock(mut);
cv.wait(lock);
if (completed_flag) break;
cout << "consumer " << this_thread::get_id() << " hello kitty\n";
}
lock_guard<mutex> lock(mut);
cout << "consumer " << this_thread::get_id() << " finished\n";
}
int main() {
thread p1(producer);
vector <thread> consumers;
for (int i = 0; i < 10; i++)
consumers.push_back(thread(consumer));
p1.join();
for (auto& i : consumers)
i.join();
}
// consumer 24124 hello kitty
// consumer 10700 hello kitty
// consumer 23704 hello kitty
// consumer 3988 hello kitty
// consumer 17432 hello kitty
// consumer 21176 hello kitty
// consumer 18976 hello kitty
// consumer 24028 hello kitty
// consumer 24044 hello kitty
// producer finished
// consumer 22840 finished
// consumer 24028 finished
// consumer 3988 finished
// consumer 21176 finished
// consumer 23704 finished
// consumer 24044 finished
// consumer 24124 finished
// consumer 10700 finished
// consumer 17432 finished
// consumer 18976 finished
Spurious wakeups
In computing, a spurious wakeup occurs when a thread wakes up from waiting on a condition variable and finds that the condition is still unsatisfied. It is referred to as spurious because the thread has seemingly been awoken for no reason. Spurious wakeups usually happen because in between the time when the condition variable was signaled and when the awakened thread was finally able to run, another thread ran first and changed the condition again. In general, if multiple threads are awakened, the first one to run will find the condition satisfied, but the others may find the condition unsatisfied. In this way, there is a race condition between all the awakened threads. The first thread to run will win the race and find the condition satisfied, while the other threads will lose the race, and experience a spurious wakeup.
https://en.wikipedia.org/wiki/Spurious_wakeup
Future & Promise
Threadler arasında değişken göndermek için kullanıyoruz.
- Promise : Değeri üreten thread bu değişkeni alır
- Future : Değeri kullanacak thread bu değişkeni alır.
#include <iostream>
#include <thread>
#include <future>
using namespace std;
void func(promise<double>& num) {
this_thread::sleep_for(chrono::seconds(2)); // Simulate some work
num.set_value(5.3);
}
int main() {
promise<double> p;
future<double> f = p.get_future();
thread t(func, ref(p));
t.detach();
cout << f.get() << endl; // 5.3
}- promise::set_value() ile değeri set ediyoruz.
- future::get() ile set edilen değeri alıyoruz.
- promise::get_future() Returns a future object associated with the same shared state as *this.
Programı çalıştırdığımızda 2 saniye bekleyip sonra ekrana 5.3 çıktısını verecektir. f.get() yaptığında o değer atanana kadar bekliyor.
Future a ait diğer bazı fonksiyonlar:
| S. No. | Function | Description |
|---|---|---|
| 1 | get() | Function to get the value received. |
| 2 | wait() | This function tells the compiler to wait() for the process to be done. |
| 3 | wait_for() | This function tells the compiler to wait() for the specified time duration for the process to be finished. |
| 4 | wait_until() | This function tells the compiler to wait() till the specified time duration for the process to be finished. |
| 5 | valid() | This function checks if the future() object has a shared state i.e. is it valid to perform get() operation. |
sadece belli bir süre boyunca beklemek istiyorsak:
#include <iostream>
#include <thread>
#include <future>
using namespace std;
void func(promise<double>& num) {
this_thread::sleep_for(chrono::seconds(2)); // Simulate some work
num.set_value(5.3);
}
int main() {
promise<double> p;
future<double> f = p.get_future();
thread t(func, ref(p));
t.detach();
auto status = f.wait_for(chrono::seconds(1));
if (status == future_status::ready)
cout << f.get() << endl;
else if (status == future_status::timeout)
cout << "timeout" << endl;
}
// timeoutshared_future
future gibi tek farkı ise normalde future 1 consumer ile kullanılabilir ama shared future n consumer ile kullanılabilir.
#include <iostream>
#include <thread>
#include <future>
using namespace std;
void producer(promise<int>& num) {
this_thread::sleep_for(chrono::seconds(2)); // Simulate some work
num.set_value(5);
}
void consumer(shared_future<int>& sf) {
cout << sf.get() << endl;
}
int main() {
promise<int> p;
shared_future<int> sf = p.get_future();
thread t(producer, ref(p));
t.detach();
thread consumer_thread0(consumer, ref(sf));
thread consumer_thread1(consumer, ref(sf));
thread consumer_thread2(consumer, ref(sf));
consumer_thread0.join();
consumer_thread1.join();
consumer_thread2.join();
}
// 5
// 5
// 5
Leave a Reply