I need to do proper synchronization over several threads in my application. The threads are devided into a group of threads - graup A which may contain more then one thread and thread B. Thread B is supposed to be unlocker thread while only one thread from group A at the same time is supposed to be unlocked by thread B. I tryied to achive stable solution using pthread_mutex_t with code like this:
// thread group A
...
while(...)
{
pthread_mutex_lock(&lock) ;
// only one thread at the same time allowed from here
...
}
// thread B
while(...)
{
pthread_mutex_unlock(&lock)
...
}
...
int main()
{
...
pthread_mutex_init(&lock, NULL) ;
pthread_mutex_lock(&lock) ;
...
// start threads
...
}
This solution works but is unstable and sometimes causes deadlock because if it happens that
pthread_mutex_unlock(&lock) ;
is called before
pthread_mutex_lock(&lock) ;
then mutex stays locked and causes deadlock because
pthread_mutex_unlock(&lock) ;
has no effect if it is called before
pthread_mutex_lock(&lock) ;
I found one crappy solution to this but it's crappy because it eats additional cpu time needlessly. Such solution is this:
bool lock_cond ;
// thread group A
...
while(...)
{
lock_cond = true ;
pthread_mutex_lock(&lock) ;
lock_cond = false ;
// only one thread at the same time allowed from here
...
}
// thread B
while(...)
{
while(!lock_cond)
;
pthread_mutex_unlock(&lock)
...
}
...
int main()
{
...
pthread_mutex_init(&lock, NULL) ;
pthread_mutex_lock(&lock) ;
...
// start threads
...
}
So my question is how to properly implement threads synchronization in such scenario ?. Can I use
pthread_mutex_t
variables for that or does I have to use semaphore ?
Please explain with code examples.
There are many kinds of synchronization patterns between different threads.
Your scenario seems to be a good fit for a binary semaphore rather than a mutex:
Thread B doesn't "lock and release" - it just signals threads in the A group that they may proceed with their work.
It's not clear that a thread in A, once done with its own work, allows other threads in A to start work.
C++ will have an std::binary_semaphore in the next language standard version. Until then, you'll need to use a C++ library implementing them (perhaps this one? I haven't tried it myself), or using POSIX semaphores in C-style coding.
After studying and modifying code samples taken from
https://en.cppreference.com/w/cpp/thread/condition_variable
for my needs I created the following:
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <unistd.h>
#include <random>
#include <ctime>
std::mutex m, m1;
std::condition_variable cv, cv1;
bool ready = false, ready2 = false;
bool processed = false;
pthread_mutex_t only_one ;
bool done, done2 ;
class Task
{
public:
void thread_groupA(std::string msg)
{
while(!done)
{
pthread_mutex_lock(&only_one) ;
{
std::lock_guard<std::mutex> lk(m1);
ready2 = true;
}
cv1.notify_one();
std::cout << msg << std::endl ;
std::cout << "before sleep 1 second" << std::endl ;
sleep(1); // sleep for demonstration that it really works
std::cout << "after sleep 1 second" << std::endl ;
std::cout << "before cv.wait()" << std::endl ;
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
pthread_mutex_unlock(&only_one) ;
std::cout << "after cv.wait()" << std::endl ;
ready = false ;
processed = true;
lk.unlock();
cv.notify_one();
int val = rand() % 10000 ;
usleep(val) ; // server clients timing simulation
// different clients provide different data so clients timing isn't the same.
// fastest client's thread gets passed through 'pthread_mutex_lock(&only_one)'
}
}
} ;
void threadB()
{
int aa = 2, bb = 0 ;
while(!done2)
{
std::unique_lock<std::mutex> lk(m1);
cv1.wait(lk, []{return ready2;});
ready2 = false ;
if(done2)
break ;
if(bb % aa)
{
std::cout << "before sleep 5 seconds" << std::endl ;
sleep(5); // sleep for demonstration that it really works
std::cout << "after sleep 5 seconds" << std::endl ;
}
{
std::lock_guard<std::mutex> lk(m);
ready = true;
}
cv.notify_one();
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
processed = false ;
}
++bb ;
}
}
int main()
{
pthread_mutex_init(&only_one, NULL) ;
done = false ;
done2 = false ;
srand(time(0)) ;
Task * taskPtr1 = new Task();
Task * taskPtr2 = new Task();
std::thread worker1(&Task::thread_groupA, taskPtr1, "thread 1");
std::thread worker2(&Task::thread_groupA, taskPtr2, "thread 2");
std::thread signal(threadB);
std::string s ;
do
{
getline(std::cin, s) ;
}
while(s.compare("stop") != 0) ;
done = true ;
worker1.join();
worker2.join();
done2 = true ;
{
std::lock_guard<std::mutex> lk(m1);
ready2 = true;
}
cv1.notify_one();
signal.join();
}
Now based on this code I can make implementation to my app. I hope this will work pretty stable.
Related
I'm wanting to have several threads all waiting on a conditional variable (CV) and when the main thread updates a variable they all execute. However, I need the main thread to wait until all these have completed before moving on. The other threads don't end and simply go back around and wait again, so I can't use thread.join() for example.
I've got the first half working, I can trigger the threads, but the main just hangs and doesn't continue. Below is my current code
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <Windows.h>
#define N 3
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
bool finished[N];
void print_id(int id) {
while (1) {
std::unique_lock<std::mutex> lck(mtx); //Try and Lock the Mutex
while (finished[id]) cv.wait(lck); //Wait until finished is false
// ...
std::cout << "thread " << id << '\n';
finished[id] = true; //Set finished to be true. When true, program should continue
}
}
int main()
{
std::thread threads[N];
// spawn 10 threads:
for (int i = 0; i < N; ++i) {
threads[i] = std::thread(print_id, i); //Create n threads
finished[i] = true; //Set default finished to be true
}
std::cout << "N threads ready to race...\n";
for (int i = 0; i < 5; i++) {
std::unique_lock<std::mutex> lck(mtx); //Lock mutex
for (int i = 0; i < N; i++) {
finished[i] = false; //Set finished to false, this will break the CV in each thread
}
cv.notify_all(); //Notify all threads
cv.wait(lck, [] {return finished[0] == true; }); //Wait until all threads have finished (but not ended)
std::cout << "finished, Sleeping for 2s\n";
Sleep(2000);
}
return 0;
}
Thank you.
Edit: I am aware I am only currently checking the status of the finished[0] and not each one. This is done just for simplicity atm and would eventually need to be all of them. I will write a function to manage this later.
You have cv.wait(lck, [] {return finished[0] == true; }); in main thread, but it is not being notified.
You'd need to notify it, and you'd better use another condition_variable for it, not the same as for worker thead notifiecation.
I am now testing std::condition_variable recently , and find it is quite different with pthread_cond_t after test , I like to know if anything in my test wrong ? or std::condition_variable is really quite different with pthread_cond_t ?
The pthread_cond_t source is the following , compiled at gcc 4.4.6 :
pthread_cond_t condA = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int ProcessRow = 0 ;
#define LOOPCNT 10
void *producer()
{
int idx ;
for(idx=0;idx<LOOPCNT;idx++)
{
//pthread_mutex_lock(&mutex);
__sync_add_and_fetch(&ProcessRow,1) ;
pthread_cond_signal(&condA);
printf("sending signal...(%d)\n",ProcessRow) ;
//pthread_mutex_unlock(&mutex);
}
printf("I am out ... \n") ;
}
void *consumer()
{
int icnt = 0 ;
while(1)
{
pthread_mutex_lock(&mutex);
while (ProcessRow <= 0)
pthread_cond_wait(&condA, &mutex);
pthread_mutex_unlock(&mutex); // I forget to add unlock to fail this test
__sync_sub_and_fetch(&ProcessRow,1) ;
++icnt ;
printf("receving=(%d)\n",ProcessRow) ;
usleep(10000) ;
}
printf("(%d)\n",ProcessRow) ;
}
The output :
sending signal...(1)
sending signal...(2)
sending signal...(3)
sending signal...(4)
sending signal...(5)
sending signal...(6)
sending signal...(7)
sending signal...(8)
sending signal...(9)
sending signal...(10)
I am out ...
receving=(9)
Look like comsumer thread block in pthread_cond_wait , so that "receving" only print
one time !!!!
and then the following test is for std::condition_variable !!!!
The following binsem.hpp comes from
https://gist.github.com/yohhoy/2156481
with a little modification , compiled at g++ 4.8.1
class binsem {
public:
explicit binsem(int init_count = count_max)
: count_(init_count) {}
// P-operation / acquire
void wait()
{
std::unique_lock<std::mutex> lk(m_);
cv_.wait(lk, [this]{ return 0 < count_; });
--count_;
}
bool try_wait()
{
std::lock_guard<std::mutex> lk(m_);
if (0 < count_)
{
--count_;
return true;
} else
{
return false;
}
}
// V-operation / release
void signal()
{
std::lock_guard<std::mutex> lk(m_);
//if (count_ < count_max) // I mark here
//{ // I mark here
++count_;
cv_.notify_one();
//} // I mark here
}
// Lockable requirements
void lock() { wait(); }
bool try_lock() { return try_wait(); }
void unlock() { signal(); }
private:
static const int count_max = 1;
int count_;
std::mutex m_;
std::condition_variable cv_;
};
and my source :
#define LOOPCNT 10
atomic<int> ProcessRow ;
void f4()
{
for(int i=0;i<LOOPCNT;i++)
{
sem2.unlock() ;
++ProcessRow ;
}
cout << "i am out" << endl ;
}
void f5()
{
int icnt = 0 ;
std::chrono::milliseconds sleepDuration(1000);
while(1)
{
sem2.lock() ;
++icnt ;
std::this_thread::sleep_for(sleepDuration);
cout << ProcessRow << "in f5 " << endl ;
--ProcessRow ;
if(icnt >= LOOPCNT)
break ;
}
printf("(%d)\n",icnt) ;
}
The output :
i am out
10in f5
9in f5
8in f5
7in f5
6in f5
5in f5
4in f5
3in f5
2in f5
1in f5
(10)
Look like signal only effect if the pthread_cond_wait is waiting!! if not , signal is losted !!
And for std::condition_variable , look like std::condition_variable.wait() will wake up the times notify_one() are called ,if you call notify_one() 10 seconds ago and then call wait() , std::condition_variable.wait() still will get that notify_one() message , quite different with pthread_cond_t !!
Am I miss something in this test ? Or just like my test , std::condition and pthread_cond_t just act like the test showes ?
Edit :
I think the following will showes more easier for this test , sorry to forget to unlock so that the test failed , they are the same behavior !!!!
int main()
{
//pthread_mutex_lock(&mutex);
++ProcessRow ;
pthread_cond_signal(&condA);
//pthread_mutex_unlock(&mutex);
printf("sending signal...\n") ;
sleep(10) ;
pthread_mutex_lock(&mutex);
while (ProcessRow <= 0)
pthread_cond_wait(&condA, &mutex);
pthread_mutex_unlock(&mutex);
printf("wait pass through\n") ;
}
This will showes :
sending signal...
wait pass through
And for std::condition_variable
int main()
{
sem2.unlock() ;
std::chrono::milliseconds sleepDuration(10000);
cout << "going sleep" << endl ;
std::this_thread::sleep_for(sleepDuration);
sem2.lock() ;
cout << "lock pass through " << endl ;
}
Will showes :
going sleep
lock pass through
So it is my fault to do the test wrong , cause to deadlock !!! Thanks for all great advice!
In your pthread code, you never unlock the mutex, The consumer() function deadlocks on the second iteration. Also, the outer while loop should break out when some condition is satisfied. I suggest that it should break out when icnt reaches the LOOPCNT. This sort of matches how you break the loop in f5().
void *consumer(void *x)
{
int icnt = 0 ;
while(1)
{
pthread_mutex_lock(&mutex);
while (ProcessRow <= 0)
pthread_cond_wait(&condA, &mutex);
__sync_sub_and_fetch(&ProcessRow,1) ;
++icnt ;
printf("receving=(%d) icnt=(%d)\n",ProcessRow, icnt) ;
pthread_mutex_unlock(&mutex);
if (icnt == LOOPCNT) break;
usleep(10000) ;
}
printf("(%d)\n",ProcessRow) ;
}
It doesn't seem like your std::thread version of the code closely matches the pthread version at all, so I don't think you can compare their executions in this way. Instead of mimicking a semaphore, I think it better to just use the std::condition_variable exactly like you use it in the pthread version of the code. This way, you can really compare apples to apples.
std::condition_variable condA;
std::mutex mutex;
volatile int ProcessRow = 0 ;
#define LOOPCNT 10
void producer()
{
int idx ;
for(idx=0;idx<LOOPCNT;idx++)
{
std::unique_lock<std::mutex> lock(mutex);
__sync_add_and_fetch(&ProcessRow,1) ;
condA.notify_one();
printf("sending signal...(%d)\n",ProcessRow) ;
}
printf("I am out ... \n") ;
}
void consumer()
{
int icnt = 0 ;
while(icnt < LOOPCNT)
{
if(icnt > 0) usleep(10000);
std::unique_lock<std::mutex> lock(mutex);
while (ProcessRow <= 0)
condA.wait(lock);
__sync_sub_and_fetch(&ProcessRow,1) ;
++icnt ;
printf("receving=(%d) icnt=(%d)\n",ProcessRow, icnt) ;
}
printf("(%d)\n",ProcessRow) ;
}
Both pthread_cond_t and std::condition_variable work the same way. They are stateless and a signal can only get "lost" if no thread is blocked, in which case no signal is needed because there is no thread that needs one.
How can I check if a std::thread is still running (in a platform independent way)?
It lacks a timed_join() method and joinable() is not meant for that.
I thought of locking a mutex with a std::lock_guard in the thread and using the try_lock() method of the mutex to determine if it is still locked (the thread is running), but it seems unnecessarily complex to me.
Do you know a more elegant method?
Update: To be clear: I want to check if the thread cleanly exited or not. A 'hanging' thread is considered running for this purpose.
If you are willing to make use of C++11 std::async and std::future for running your tasks, then you can utilize the wait_for function of std::future to check if the thread is still running in a neat way like this:
#include <future>
#include <thread>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono_literals;
/* Run some task on new thread. The launch policy std::launch::async
makes sure that the task is run asynchronously on a new thread. */
auto future = std::async(std::launch::async, [] {
std::this_thread::sleep_for(3s);
return 8;
});
// Use wait_for() with zero milliseconds to check thread status.
auto status = future.wait_for(0ms);
// Print status.
if (status == std::future_status::ready) {
std::cout << "Thread finished" << std::endl;
} else {
std::cout << "Thread still running" << std::endl;
}
auto result = future.get(); // Get result.
}
If you must use std::thread then you can use std::promise to get a future object:
#include <future>
#include <thread>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono_literals;
// Create a promise and get its future.
std::promise<bool> p;
auto future = p.get_future();
// Run some task on a new thread.
std::thread t([&p] {
std::this_thread::sleep_for(3s);
p.set_value(true); // Is done atomically.
});
// Get thread status using wait_for as before.
auto status = future.wait_for(0ms);
// Print status.
if (status == std::future_status::ready) {
std::cout << "Thread finished" << std::endl;
} else {
std::cout << "Thread still running" << std::endl;
}
t.join(); // Join thread.
}
Both of these examples will output:
Thread still running
This is of course because the thread status is checked before the task is finished.
But then again, it might be simpler to just do it like others have already mentioned:
#include <thread>
#include <atomic>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono_literals;
std::atomic<bool> done(false); // Use an atomic flag.
/* Run some task on a new thread.
Make sure to set the done flag to true when finished. */
std::thread t([&done] {
std::this_thread::sleep_for(3s);
done = true;
});
// Print status.
if (done) {
std::cout << "Thread finished" << std::endl;
} else {
std::cout << "Thread still running" << std::endl;
}
t.join(); // Join thread.
}
Edit:
There's also the std::packaged_task for use with std::thread for a cleaner solution than using std::promise:
#include <future>
#include <thread>
#include <chrono>
#include <iostream>
int main() {
using namespace std::chrono_literals;
// Create a packaged_task using some task and get its future.
std::packaged_task<void()> task([] {
std::this_thread::sleep_for(3s);
});
auto future = task.get_future();
// Run task on new thread.
std::thread t(std::move(task));
// Get thread status using wait_for as before.
auto status = future.wait_for(0ms);
// Print status.
if (status == std::future_status::ready) {
// ...
}
t.join(); // Join thread.
}
An easy solution is to have a boolean variable that the thread sets to true on regular intervals, and that is checked and set to false by the thread wanting to know the status. If the variable is false for to long then the thread is no longer considered active.
A more thread-safe way is to have a counter that is increased by the child thread, and the main thread compares the counter to a stored value and if the same after too long time then the child thread is considered not active.
Note however, there is no way in C++11 to actually kill or remove a thread that has hanged.
Edit How to check if a thread has cleanly exited or not: Basically the same technique as described in the first paragraph; Have a boolean variable initialized to false. The last thing the child thread does is set it to true. The main thread can then check that variable, and if true do a join on the child thread without much (if any) blocking.
Edit2 If the thread exits due to an exception, then have two thread "main" functions: The first one have a try-catch inside which it calls the second "real" main thread function. This first main function sets the "have_exited" variable. Something like this:
std::atomic<bool> thread_done = false;
void *thread_function(void *arg)
{
void *res = nullptr;
try
{
res = real_thread_function(arg);
}
catch (...)
{
}
thread_done = true;
return res;
}
This simple mechanism you can use for detecting finishing of a thread without blocking in join method.
std::thread thread([&thread]() {
sleep(3);
thread.detach();
});
while(thread.joinable())
sleep(1);
You can always check if the thread's id is different than std::thread::id() default constructed.
A Running thread has always a genuine associated id.
Try to avoid too much fancy stuff :)
Create a mutex that the running thread and the calling thread both have access to. When the running thread starts it locks the mutex, and when it ends it unlocks the mutex. To check if the thread is still running, the calling thread calls mutex.try_lock(). The return value of that is the status of the thread. (Just make sure to unlock the mutex if the try_lock worked)
One small problem with this, mutex.try_lock() will return false between the time the thread is created, and when it locks the mutex, but this can be avoided using a slightly more complex method.
Surely have a mutex-wrapped variable initialised to false, that the thread sets to true as the last thing it does before exiting. Is that atomic enough for your needs?
I checked both systems:
-Using thread+atomic: take 9738 milliseconds
-Using future+async: take 7746 milliseconds
Not threads: 56000milliseconds
Using a Core-I7 6 cores laptop
My code creates 4000 threads, but no more than 12 running every time.
Here is the code:
#include <iostream>
#include <thread>
#include <future>
#include <chrono>
#include <mutex> // std::mutex
#include <atomic>
#include <chrono>
#pragma warning(disable:4996)
#pragma warning(disable:6031)
#pragma warning(disable:6387)//strout
#pragma warning(disable:26451)
using namespace std;
const bool FLAG_IMPRIME = false;
const int MAX_THREADS = 12;
mutex mtx; // mutex for critical section
atomic <bool> th_end[MAX_THREADS];
atomic <int> tareas_acabadas;
typedef std::chrono::high_resolution_clock t_clock; //SOLO EN WINDOWS
std::chrono::time_point<t_clock> start_time, stop_time; char null_char;
void timer(const char* title = 0, int data_size = 1) { stop_time = t_clock::now(); double us = (double)chrono::duration_cast<chrono::microseconds>(stop_time - start_time).count(); if (title) printf("%s time = %7lgms = %7lg MOPs\n", title, (double)us * 1e-3, (double)data_size / us); start_time = t_clock::now(); }
class c_trim
{
char line[200];
thread th[MAX_THREADS];
double th_result[MAX_THREADS];
int th_index;
double milliseconds_commanded;
void hilo(int hindex,int milliseconds, double& milliseconds2)
{
sprintf(line, "%i:%ia ",hindex, milliseconds); imprime(line);
this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
milliseconds2 = milliseconds * 1000;
sprintf(line, "%i:%ib ", hindex, milliseconds); imprime(line);
tareas_acabadas++; th_end[hindex] = true;
}
int wait_first();
void imprime(char* str) { if (FLAG_IMPRIME) { mtx.lock(); cout << str; mtx.unlock(); } }
public:
void lanzatareas();
vector <future<void>> futures;
int wait_first_future();
void lanzatareas_future();//usa future
};
int main()
{
c_trim trim;
timer();
trim.lanzatareas();
cout << endl;
timer("4000 tareas using THREAD+ATOMIC:", 4000);
trim.lanzatareas_future();
cout << endl;
timer("4000 tareas using FUTURE:", 4000);
cout << endl << "Tareas acabadas:" << tareas_acabadas << endl;
cout << "=== END ===\n"; (void)getchar();
}
void c_trim::lanzatareas()
{
th_index = 0;
tareas_acabadas = 0;
milliseconds_commanded = 0;
double *timeout=new double[MAX_THREADS];
int i;
for (i = 0; i < MAX_THREADS; i++)
{
th_end[i] = true;
th_result[i] = timeout[i] = -1;
}
for (i = 0; i < 4000; i++)
{
int milliseconds = 5 + (i % 10) * 2;
{
int j = wait_first();
if (th[j].joinable())
{
th[j].join();
th_result[j] = timeout[j];
}
milliseconds_commanded += milliseconds;
th_end[j] = false;
th[j] = thread(&c_trim::hilo, this, j, milliseconds, std::ref(timeout[j]));
}
}
for (int j = 0; j < MAX_THREADS; j++)
if (th[j].joinable())
{
th[j].join();
th_result[j] = timeout[j];
}
delete[] timeout;
cout <<endl<< "Milliseconds commanded to wait=" << milliseconds_commanded << endl;
}
void c_trim::lanzatareas_future()
{
futures.clear();
futures.resize(MAX_THREADS);
tareas_acabadas = 0;
milliseconds_commanded = 0;
double* timeout = new double[MAX_THREADS];
int i;
for (i = 0; i < MAX_THREADS; i++)
{
th_result[i] = timeout[i] = -1;
}
for (i = 0; i < 4000; i++)
{
int milliseconds = 5 + (i % 10) * 2;
{
int j;
if (i < MAX_THREADS) j = i;
else
{
j = wait_first_future();
futures[j].get();
th_result[j] = timeout[j];
}
milliseconds_commanded += milliseconds;
futures[j] = std::async(std::launch::async, &c_trim::hilo, this, j, milliseconds, std::ref(timeout[j]));
}
}
//Last MAX_THREADS:
for (int j = 0; j < MAX_THREADS; j++)
{
futures[j].get();
th_result[j] = timeout[j];
}
delete[] timeout;
cout << endl << "Milliseconds commanded to wait=" << milliseconds_commanded << endl;
}
int c_trim::wait_first()
{
int i;
while (1)
for (i = 0; i < MAX_THREADS; i++)
{
if (th_end[i] == true)
{
return i;
}
}
}
//Espera que acabe algun future y da su index
int c_trim::wait_first_future()
{
int i;
std::future_status status;
while (1)
for (i = 0; i < MAX_THREADS; i++)
{
status = futures[i].wait_for(0ms);
if (status == std::future_status::ready)
return i;
}
}
I also had this problem very recently. Tried with the C++20 std::jthread using the shared-stop state to check if the thread is over, but inside the thread the std::stop_token argument is a readonly and doesn't indicate to outside when the thread finishes.
So I created a simple class (nes::uthread) extending std::thread with a flag to indicate it's finished. Example:
#include <atomic>
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>
namespace nes {
class uthread final
{
std::unique_ptr<std::atomic<bool>> m_finished;
std::thread m_thr;
public:
uthread()
: m_finished { std::make_unique<std::atomic<bool>>(true) }
{}
template <class Function, class... Args>
uthread(Function&& f, Args&&... args)
: m_finished { std::make_unique<std::atomic<bool>>(false) }
, m_thr {
[](std::atomic<bool>& finished, Function&& ff, Args&&... aargs) {
try {
std::forward<Function>(ff)(std::forward<Args>(aargs)...);
finished = true;
} catch (...) {
finished = true;
throw;
}
},
std::ref(*m_finished), std::forward<Function>(f),
std::forward<Args>(args)...
}
{}
uthread(const uthread&) = delete;
uthread(uthread&&) = default;
uthread& operator=(const uthread&) = delete;
uthread& operator=(uthread&&) = default;
[[nodiscard]] std::thread::id get_id() const noexcept {
return m_thr.get_id(); }
[[nodiscard]] bool joinable() const noexcept { return m_thr.joinable(); }
void join() { m_thr.join(); }
[[nodiscard]] const std::atomic<bool>& finished() const noexcept {
return *m_finished; }
};
}
int main()
{
using namespace std;
using namespace std::chrono;
using namespace std::chrono_literals;
using namespace nes;
{
cout << "std::thread join() termination\n";
atomic<bool> finished = false;
thread t { [&finished] {
this_thread::sleep_for(2s);
finished = true;
cout << "thread ended\n";
}};
for (int i = 0; i < 5; i++) {
cout << t.get_id() << ".join() " << t.joinable()
<< " finished: " << finished << '\n';
this_thread::sleep_for(1s);
}
t.join();
}
cout << '\n';
{
cout << "std::jthread join() termination\n";
jthread t {[](stop_token st) {
this_thread::sleep_for(2s);
cout << "thread ended. stop possible: " << st.stop_possible() << '\n';
}};
auto st = t.get_stop_source();
for (int i = 0; i < 5; i++) {
cout << t.get_id() << ".join() " << t.joinable()
<< " finished: " << !st.stop_possible() << '\n';
this_thread::sleep_for(1s);
}
}
cout << '\n';
{
cout << "nes::uthread join() termination\n";
uthread t {[] {
this_thread::sleep_for(2s);
cout << "thread ended\n";
}};
for (int i = 0; i < 5; i++) {
cout << t.get_id() << ".join() " << t.joinable()
<< " finished: " << t.finished() << '\n';
this_thread::sleep_for(1s);
}
t.join();
}
}
Possible prints:
std::thread join() termination
2.join() 1 finished: 0
2.join() 1 finished: 0
thread ended
2.join() 1 finished: 1
2.join() 1 finished: 1
2.join() 1 finished: 1
std::jthread join() termination
3.join() 1 finished: 0
3.join() 1 finished: 0
thread ended. stop possible: 1
3.join() 1 finished: 0
3.join() 1 finished: 0
3.join() 1 finished: 0
nes::uthread join() termination
4.join() 1 finished: 0
4.join() 1 finished: 0
thread ended
4.join() 1 finished: 1
4.join() 1 finished: 1
4.join() 1 finished: 1
You can use std::jthread in nes::uthread so you don't need to join.
I have NUM_THREADS threads, with the following codes in my thread:
/*
Calculate some_value;
*/
//Critical section to accummulate all thresholds
{
boost::mutex::scoped_lock lock(write_mutex);
T += some_value;
num_threads++;
if (num_threads == NUM_THREADS){
T = T/NUM_THREADS;
READY = true;
cond.notify_all();
num_threads = 0;
}
}
//Wait for average threshold to be ready
if (!READY)
{
boost::unique_lock<boost::mutex> lock(wait_mutex);
while (!READY){
cond.wait(lock);
}
}
//End critical section
/*
do_something;
*/
Basically, I want all the threads to wait for the READY signal before continuing. num_thread is set to 0, and READY is false before threads are created. Once in a while, deadlock occurs. Can anyone help please?
All the boost variables are globally declared as follows:
boost::mutex write_mutex;
boost::mutex wait_mutex;
boost::condition cond;
The code has a race condition on the READY flag (which I assume is just a bool variable). What may happen (i.e. one possible variant of thread execution interleaving) is:
Thread T1: Thread T2:
if (!READY)
{
unique_lock<mutex> lock(wait_mutex); mutex::scoped_lock lock(write_mutex);
while (!READY) /* ... */
{ READY = true;
/* !!! */ cond.notify_all();
cond.wait(lock);
}
}
The code testing the READY flag is not synchronized with the code setting it (note the locks are different for these critical sections). And when T1 is in a "hole" between the flag test and waiting at cond, T2 may set the flag and send a signal to cond which T1 may miss.
The simplest solution is to lock the right mutex for the update of READY and condition notification:
/*...*/
T = T/NUM_THREADS;
{
boost::mutex::scoped_lock lock(wait_mutex);
READY = true;
cond.notify_all();
}
It looks like Boost.Thread's barriers might be what you need.
Here's a working example that averages values provided by several worker threads. Each worker thread uses the same shared barrier (via the accumulator instance) to synchronize each other.
#include <cstdlib>
#include <iostream>
#include <vector>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
boost::mutex coutMutex;
typedef boost::lock_guard<boost::mutex> LockType;
class Accumulator
{
public:
Accumulator(int count) : barrier_(count), sum_(0), count_(count) {}
void accumulateAndWait(float value)
{
{
// Increment value
LockType lock(mutex_);
sum_ += value;
}
barrier_.wait(); // Wait for other the threads to wait on barrier.
}
void wait() {barrier_.wait();} // Wait on barrier without changing sum.
float sum() {LockType lock(mutex_); return sum_;} // Return current sum
float average() {LockType lock(mutex_); return sum_ / count_;}
// Reset the sum. The barrier is automatically reset when triggered.
void reset() {LockType lock(mutex_); sum_ = 0;}
private:
typedef boost::lock_guard<boost::mutex> LockType;
boost::barrier barrier_;
boost::mutex mutex_;
float sum_;
int count_;
};
/* Posts a value for the accumulator to add and waits for other threads
to do the same. */
void workerFunction(Accumulator& accumulator)
{
// Sleep for a random amount of time before posting value
int randomMilliseconds = std::rand() % 3000;
boost::posix_time::time_duration randomDelay =
boost::posix_time::milliseconds(randomMilliseconds);
boost::this_thread::sleep(randomDelay);
// Post some random value
float value = std::rand() % 100;
{
LockType lock(coutMutex);
std::cout << "Thread " << boost::this_thread::get_id() << " posting "
<< value << " after " << randomMilliseconds << "ms\n";
}
accumulator.accumulateAndWait(value);
float avg = accumulator.average();
// Print a message to indicate this thread is past the barrier.
{
LockType lock(coutMutex);
std::cout << "Thread " << boost::this_thread::get_id() << " unblocked. "
<< "Average = " << avg << "\n" << std::flush;
}
}
int main()
{
int workerThreadCount = 5;
Accumulator accumulator(workerThreadCount);
// Create and launch worker threads
boost::thread_group threadGroup;
for (int i=0; i<workerThreadCount; ++i)
{
threadGroup.create_thread(
boost::bind(&workerFunction, boost::ref(accumulator)));
}
// Wait for all worker threads to finish
threadGroup.join_all();
{
LockType lock(coutMutex);
std::cout << "All worker threads finished\n" << std::flush;
}
/* Pause a bit before exiting, to give worker threads a chance to
print their messages. */
boost::this_thread::sleep(boost::posix_time::seconds(1));
}
I get the following output:
Thread 0x100100f80 posting 72 after 1073ms
Thread 0x100100d30 posting 44 after 1249ms
Thread 0x1001011d0 posting 78 after 1658ms
Thread 0x100100ae0 posting 23 after 1807ms
Thread 0x100101420 posting 9 after 1930ms
Thread 0x100101420 unblocked. Average = 45.2
Thread 0x100100f80 unblocked. Average = 45.2
Thread 0x100100d30 unblocked. Average = 45.2
Thread 0x1001011d0 unblocked. Average = 45.2
Thread 0x100100ae0 unblocked. Average = 45.2
All worker threads finished
I am using boost::thread, and I meet some problems.
The thing is, are there any ways I can join a thread before the last join finish?
for example,
int id=1;
void temp()
{
int theardID = id++;
for(int i=0;i<3;i++)
{
cout<<theardID << " : "<<i<<endl;
boost::this_thread::sleep(boost::posix_time::millisec(100));
}
}
int main(void)
{
boost::thread thrd1(temp);
thrd1.join();
boost::thread thrd2(temp);
boost::thread thrd3(temp);
thrd2.join();
thrd3.join();
return 0;
}
In this simple example, the order of output may be:
1:0
1:1
1:2
2:0
3:0
3:1
2:1
2:2
3:2
As the above example, we can see find out that thrd2 and thrd3 start to run after thrd1 finish.
Are there any ways to let thrd2 and thrd3 run before thrd1 finish?
You can use Boost.Thread's condition variables to synchronize on a condition more complex than what join can provide. Here's a example based on yours:
#include <iostream>
#include <boost/thread.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
boost::mutex mutex;
boost::condition_variable cond;
// These three variables protected by mutex
bool finishedFlag = false;
int finishedID = 0;
int finishedCount = 0;
int id=1;
void temp()
{
int threadID = id++;
for(int i=0;i<3;i++)
{
std::cout << threadID << " : " << i << std::endl;
boost::this_thread::sleep(boost::posix_time::millisec(100));
}
{
boost::lock_guard<boost::mutex> lock(mutex);
finishedFlag = true;
finishedID = threadID;
++finishedCount;
}
cond.notify_one();
}
int main(void)
{
boost::thread thrd1(temp);
boost::this_thread::sleep(boost::posix_time::millisec(300));
boost::thread thrd2(temp);
boost::thread thrd3(temp);
boost::unique_lock<boost::mutex> lock(mutex);
while (finishedCount < 3)
{
while (finishedFlag != true)
{
// mutex is released while we wait for cond to be signalled.
cond.wait(lock);
// mutex is reacquired as soon as we finish waiting.
}
finishedFlag = false;
if (finishedID == 1)
{
// Do something special about thrd1 finishing
std::cout << "thrd1 finished" << std::endl;
}
};
// All 3 threads finished at this point.
return 0;
}
The join function means "stop this thread until that thread finishes." It's a simple tool for a simple purpose: ensuring that, past this point in the code, thread X is finished.
What you want to do isn't a join operation at all. What you want is some kind of synchronization primitive to communicate and synchronize behavior between threads. Boost.Thread has a number of alternatives for synchronization, from conditions to mutexes.