I am trying to solve the problem with a time derivation in a multithreaded setup. I have 3 threads, all pinned to different cores. The first two threads (reader_threads.cc) run in the infinite while loop inside the run() function. They finish their execution and send the current time window they are into the third thread.
The current time window is calculated based on the value from chrono time / Ti
The third thread is running at its own pace, and it's checking only the request when the flag has been raised, which is also sent via Message to the third thread.
I was able to get the desired behavior of all three threads in the same epoch if one epoch is at least 20000us. In the results, you can find more info.
Reader threads
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <chrono>
#include <atomic>
#include <mutex>
#include "control_thread.h"
#define INTERNAL_THREAD
#if defined INTERNAL_THREAD
#include <thread>
#include <pthread.h>
#else
#endif
using namespace std;
atomic<bool> thread_active[2];
atomic<bool> go;
pthread_barrier_t barrier;
template <typename T>
void send(Message volatile * m, unsigned int epoch, bool flag) {
for (int i = 0 ; i < sizeof(T); i++){
m->epoch = epoch;
m->flag = flag;
}
}
ControlThread * ct;
// Main run for threads
void run(unsigned int threadID){
// Put message into incoming buffer
Message volatile * m1 = &(ct->incoming_requests[threadID - 1]);
thread_active[threadID] = true;
std::atomic<bool> flag;
// this thread is done initializing stuff
thread_active[threadID] = true;
while (!go);
while(true){
using namespace std::chrono;
// Get current time with precision of microseconds
auto now = time_point_cast<microseconds>(steady_clock::now());
// sys_microseconds is type time_point<system_clock, microseconds>
using sys_microseconds = decltype(now);
// Convert time_point to signed integral type
auto duration = now.time_since_epoch();
// Convert signed integral type to time_point
sys_microseconds dt{microseconds{duration}};
// test
if (dt != now){
std::cout << "Failure." << std::endl;
}else{
// std::cout << "Success." << std::endl;
}
auto epoch = duration / Ti;
pthread_barrier_wait(&barrier);
flag = true;
// send current time to the control thread
send<int>(m1, epoch, flag);
auto current_position = duration % Ti;
std::chrono::duration<double, micro> multi_thread_sleep = chrono::microseconds(Ti) - chrono::microseconds(current_position);
if(multi_thread_sleep > chrono::microseconds::zero()){
this_thread::sleep_for(multi_thread_sleep);
}
}
}
int threads_num = 3;
void server() {
// Don't start control thread until reader threds finish init
for (int i=1; i < threads_num; i++){
while (!thread_active[i]);
}
go = true;
while (go) {
for (int i = 0; i < threads_num; i++) {
ct->current_requests(i);
}
// Arbitrary sleep to ensure that locking is accurate
std::this_thread::sleep_for(50us);
}
}
class Thread {
public:
#if defined INTERNAL_THREAD
thread execution_handle;
#endif
unsigned int id;
Thread(unsigned int i) : id(i) {}
};
void init(){
ct = new ControlThread();
}
int main (int argc, char * argv[]){
Thread * r[4];
pthread_barrier_init(&barrier, NULL, 2);
init();
/* start threads
*================*/
for (unsigned int i = 0; i < threads_num; i++) {
r[i] = new Thread(i);
#if defined INTERNAL_THREAD
if(i==0){
r[0]->execution_handle = std::thread([] {server();});
}else if(i == 1){
r[i]->execution_handle = std::thread([i] {run(i);});
}else if(i == 2){
r[i]->execution_handle = std::thread([i] {run(i);});
}
/* pin to core i */
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(i, &cpuset);
int rc = pthread_setaffinity_np(r[i]->execution_handle.native_handle(), sizeof(cpuset), &cpuset);
#endif
}
// wait for threads to end
for (unsigned int i = 0; i < threads_num + 1; i++) {
#if defined INTERNAL_THREAD
r[i]->execution_handle.join();
#endif
}
pthread_barrier_destroy(&barrier);
return 0;
}
Control Thread
#ifndef __CONTROL_THEAD_H__
#define __CONTROL_THEAD_H__
// Global vars
const auto Ti = std::chrono::microseconds(15000);
std::mutex m;
int count;
class Message{
public:
std::atomic<bool> flag;
unsigned long epoch;
};
class ControlThread {
public:
/* rw individual threads */
Message volatile incoming_requests[4];
void current_requests(unsigned long current_thread) {
using namespace std::chrono;
auto now = time_point_cast<microseconds>(steady_clock::now());
// sys_milliseconds is type time_point<system_clock, milliseconds>
using sys_microseconds = decltype(now);
// Convert time_point to signed integral type
auto time = now.time_since_epoch();
// Convert signed integral type to time_point
sys_microseconds dt{microseconds{time}};
// test
if (dt != now){
std::cout << "Failure." << std::endl;
}else{
// std::cout << "Success." << std::endl;
}
long contol_thread_epoch = time / Ti;
// Only check request when flag is raised
if(incoming_requests[current_thread].flag){
m.lock();
incoming_requests[current_thread].flag = false;
m.unlock();
// If reader thread epoch and control thread matches
if(incoming_requests[current_thread].epoch == contol_thread_epoch){
// printf("Successful desired behaviour\n");
}else{
count++;
if(count > 0){
printf("Missed %d\n", count);
}
}
}
}
};
#endif
RUN
g++ -std=c++2a -pthread -lrt -lm -lcrypt reader_threads.cc -o run
sudo ./run
Results
The following missed epochs are with one loop iteration (single Ti) equal to 1000us. Also, by increasing Ti, the less number of epochs have been skipped. Finally, if Ti is set to the 20000 us , no skipped epochs are detected. Does anyone have an idea whether I am making a mistake in casting or in communication between threads? Why the threads are not in sync if epoch is i.e. 5000us?
Missed 1
Missed 2
Missed 3
Missed 4
Missed 5
Missed 6
Missed 7
Missed 8
Missed 9
Missed 10
Missed 11
Missed 12
Missed 13
Missed 14
Missed 15
Missed 16
Related
I have a task to compute Pi with following formula:
(i is in range from 0 to N, N = 10^8)
Computation should be completed in multiple threads with following requirement: each thread receives only a small fixed amount of computations to complete (in my case - 40 sum members at a time), and there should be a "Task pool" which gives new set of computations into a thread when it reports completion of previous set of operations given to it. Before a thread receives new task, it should wait. All of this should be done with WinAPI.
My solution is this class:
#include "ThreadManager.h"
#include <string>
HANDLE ThreadManager::mutex = (CreateMutexA(nullptr, true, "m"));
ThreadManager::ThreadManager(size_t threadCount)
{
threads.reserve(threadCount);
for (int i = 0; i < threadCount; i++)
{
threadInfo.push_back(new ThreadStruct(i * OP_COUNT));
HANDLE event = CreateEventA(nullptr, false, true, std::to_string(i).c_str());
if (event)
{
threadEvents.push_back(event);
DuplicateHandle(GetCurrentProcess(), event, GetCurrentProcess(),
&(threadInfo[i]->threadEvent), 0, false, DUPLICATE_SAME_ACCESS);
}
else std::cout << "Unknown error: " << GetLastError() << std::endl;
HANDLE thread = CreateThread(nullptr, 0,
reinterpret_cast<LPTHREAD_START_ROUTINE>(&ThreadManager::threadFunc),
threadInfo[i],
CREATE_SUSPENDED, nullptr);
if (thread) threads.push_back(thread);
else std::cout << "Unknown error: " << GetLastError() << std::endl;
}
}
double ThreadManager::run()
{
size_t operations_done = threads.size() * OP_COUNT;
for (HANDLE t : threads) ResumeThread(t);
DWORD index;
Sleep(10);
while (operations_done < ThreadManager::N)
{
ReleaseMutex(ThreadManager::mutex);
index = WaitForMultipleObjects(this->threadEvents.size(), this->threadEvents.data(), false, 10000);
WaitForSingleObject(ThreadManager::mutex, 1000);
threadInfo[index] -> operationIndex = operations_done + OP_COUNT;
SetEvent(threadEvents[index]);
//std::cout << "Operations completed: " << operations_done << "/1000" << std::endl;
operations_done += OP_COUNT;
}
long double res_pi = 0;
for (auto&& ts: this->threadInfo)
{
res_pi += ts->pi;
ts->operationIndex = N;
}
res_pi /= N;
WaitForMultipleObjects(this->threads.size(), this->threads.data(), true, 10000);
std::cout.precision(10);
std::cout << "Pi value for " << threads.size() << " threads: " << res_pi;
threads.clear();
return 0;
}
ThreadManager::~ThreadManager()
{
if (!threads.empty())
for (HANDLE t: threads)
{
TerminateThread(t, -1);
CloseHandle(t);
}
std::destroy(threadInfo.begin(), threadInfo.end());
}
long double ThreadManager::calc(size_t startIndex)
{
long double xi = 0;
long double pi = 0;
for (size_t i = startIndex; i < startIndex + OP_COUNT; i++)
{
const long double ld_i = i;
const long double half = 0.5f;
xi = (ld_i + half) * (1.0 / N);
pi += ((4.0 / (1.0 + xi * xi)));
}
return pi;
}
DWORD WINAPI ThreadManager::threadFunc(ThreadStruct *ts)
{
while (ts->operationIndex < N)
{
WaitForSingleObject(ts->threadEvent, 1000);
ts->pi += calc(ts->operationIndex);
WaitForSingleObject(ThreadManager::mutex, 1000);
SetEvent(ts->threadEvent);
ReleaseMutex(ThreadManager::mutex);
}
return 0;
}
ThreadStruct::ThreadStruct(size_t opIndex)
{
this -> pi = 0;
this -> operationIndex = opIndex;
}
My Idea was that there will be an auto-reset event for each thread, which is set to signaled when a thread finishes it's computation. Main thread is waiting on one of thread Events to signal, and after modifying some values in a shared ThreadStruct (to enable thread start another portion of computations) it sets that same event to signaled, which is received by the exact same thread and the process received. But this doesn't work for even one thread: as a result i see values which are pretty random and not close to Pi (like 0.0001776328265).
Though my GDB debugger was working poorly (not displaying some variables and sometimes even crashing), I noticed that there were too much computations happening (I scaled down N to 1000. Therefore, I should have seen threads printing out "computing" 1000/40 = 25 times, but actually it happened hundreds of times)
Then I tried adding a mutex so threads wait until main thread is not busy before signaling the event. That made computation much slower, and still inaccurate and random (example: 50.26492171 in case of 16 threads).
What can be the problem? Or, if it's completely wrong, how do I organize multithread calculation then? Was creating a class a bad idea?
If you want to reproduce the problem, here is header file content (I am using c++20, MinGW 6.0):
#ifndef MULTITHREADPI_THREADMANAGER_H
#define MULTITHREADPI_THREADMANAGER_H
#include <iostream>
#include <vector>
#include <list>
#include <windows.h>
#include <memory>
struct ThreadStruct
{
size_t operationIndex;
long double pi;
HANDLE threadEvent = nullptr;
explicit ThreadStruct(size_t opIndex);
};
class ThreadManager
{
public:
explicit ThreadManager(size_t threadCount);
double run();
~ThreadManager();
private:
std::vector<ThreadStruct*> threadInfo;
std::vector<HANDLE> threads;
std::vector<HANDLE> threadEvents;
static HANDLE mutex;
static long double calc(size_t startIndex);
static const int OP_COUNT = 40;
static const int N = 100000000;
static DWORD WINAPI threadFunc(ThreadStruct* ts);
};
#endif //MULTITHREADPI_THREADMANAGER_H
To execute code, just construct ThreadManager with desired number of threads as argument and call run() on it.
Even with all below changed, it doesn't give consistent values close to PI. There must be more stuff to fix. I think it has to do with the events. If I understand it correctly, there are two different things the mutex protects. And the event is also used for 2 different things. So both change their meaning during execution. This makes it very hard to think it through.
1. Timeouts
WaitForMultipleObjects may run into a timeout. In that case it returns WAIT_TIMEOUT, which is defined as 0x102 or 258. You access the threadInfo vector with that value without bounds checking. You can use at(n) for a bounds-checked version of [n].
You can easily run into a 10 second timeout when debugging or when setting OP_COUNT to high numbers. So, maybe you want to set it to INFINITE instead.
This leads to all sorts of misbehavior:
the threads information (operationIndex) is updated while the thread might work on it.
operations_done is updated although those operations may not be done
The mutex is probably overreleased
2. Limit the number of threads
The thread manager should also check the number of threads, since you can't set it to a number higher than MAXIMUM_WAIT_OBJECTS, otherwise WaitForMultipleObjects() won't work reliably.
3. Off by 1 error
Should be
size_t operations_done = (threads.size()-1) * OP_COUNT;
or
threadInfo[index] -> operationIndex = operations_done; // was + OP_COUNT
otherwise it'll skip one batch
4. Ending the threads
Ending the threads relies on the timeouts.
When you replace all timeouts by INFINITE, you'll notice that your threads never end. You need another ReleaseMutex(mutex); before
res_pi /= N;
struct CommonData;
struct ThreadData
{
CommonData* pData;
ULONG i, k;
ThreadData(CommonData* pData, ULONG i, ULONG k) : pData(pData), i(i), k(k) {}
static ULONG CALLBACK Work(void* p);
};
struct CommonData
{
HANDLE hEvent = 0;
LONG dwActiveThreadCount = 1;
ULONG N;
union {
double res = 0;
__int64 i64;
};
CommonData(ULONG N) : N(N) {}
~CommonData()
{
if (HANDLE h = hEvent)
{
CloseHandle(h);
}
}
void DecThread()
{
if (!InterlockedDecrement(&dwActiveThreadCount))
{
if (!SetEvent(hEvent)) __debugbreak();
}
}
BOOL AddThread(ULONG i, ULONG k)
{
InterlockedIncrementNoFence(&dwActiveThreadCount);
if (ThreadData* ptd = new ThreadData(this, i, k))
{
if (HANDLE hThread = CreateThread(0, 0, ThreadData::Work, ptd, 0, 0))
{
CloseHandle(hThread);
return TRUE;
}
delete ptd;
}
DecThread();
return FALSE;
}
BOOL Init()
{
return 0 != (hEvent = CreateEvent(0, 0, 0, 0));
}
void Wait()
{
DecThread();
if (WaitForSingleObject(hEvent, INFINITE) != WAIT_OBJECT_0) __debugbreak();
}
};
ULONG CALLBACK ThreadData::Work(void* p)
{
CommonData* pData = reinterpret_cast<ThreadData*>(p)->pData;
ULONG i = reinterpret_cast<ThreadData*>(p)->i;
ULONG k = reinterpret_cast<ThreadData*>(p)->k;
delete p;
ULONG N = pData->N;
double pi = 0;
do
{
double xi = (i++ + 0.5) / N;
pi += 4 / (1 + xi * xi);
} while (--k);
union {
double d;
__int64 i64;
};
i64 = pData->i64;
for (;;)
{
union {
double d_compare;
__int64 i64_compare;
};
i64_compare = i64;
d += pi;
if (i64_compare == (i64 = InterlockedCompareExchange64(
&pData->i64, i64, i64_compare)))
{
break;
}
}
pData->DecThread();
return 0;
}
double calc_pi(ULONG N)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
if (si.dwNumberOfProcessors)
{
CommonData cd(N);
if (cd.Init())
{
ULONG k = (N + si.dwNumberOfProcessors - 1) / si.dwNumberOfProcessors, i = 0;
do
{
if (!cd.AddThread(i, k))
{
break;
}
} while (i += k, --si.dwNumberOfProcessors);
cd.Wait();
if (!si.dwNumberOfProcessors)
{
return cd.res/ N;
}
}
}
return 0;
}
when i call calc_pi(100000000) on 8 core i got 3.1415926535898153
currently i am programming for an embedded application which reads values from sensors periodically. I want them to be read, every 20 ms.
Im using this tutorial
struct periodic_info {
int sig;
sigset_t alarm_sig;
};
static int make_periodic(int unsigned period, struct periodic_info *info)
{
static int next_sig;
int ret;
unsigned int ns;
unsigned int sec;
struct sigevent sigev;
timer_t timer_id;
struct itimerspec itval;
/* Initialise next_sig first time through. We can't use static
initialisation because SIGRTMIN is a function call, not a constant */
if (next_sig == 0)
next_sig = SIGRTMIN;
/* Check that we have not run out of signals */
if (next_sig > SIGRTMAX)
return -1;
info->sig = next_sig;
next_sig++;
/* Create the signal mask that will be used in wait_period */
sigemptyset(&(info->alarm_sig));
sigaddset(&(info->alarm_sig), info->sig);
/* Create a timer that will generate the signal we have chosen */
sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = info->sig;
sigev.sigev_value.sival_ptr = (void *)&timer_id;
ret = timer_create(CLOCK_MONOTONIC, &sigev, &timer_id);
if (ret == -1)
return ret;
/* Make the timer periodic */
sec = period / 1000000;
ns = (period - (sec * 1000000)) * 1000;
itval.it_interval.tv_sec = sec;
itval.it_interval.tv_nsec = ns;
itval.it_value.tv_sec = sec;
itval.it_value.tv_nsec = ns;
ret = timer_settime(timer_id, 0, &itval, NULL);
return ret;
}
static void wait_period(struct periodic_info *info)
{
int sig;
sigwait(&(info->alarm_sig), &sig);
}
static int thread_1_count;
The Main:
int main(){
pthread_t t_1;
pthread_t t_2;
sigset_t alarm_sig;
int i;
printf("Periodic threads using POSIX timers\n");
/* Block all real time signals so they can be used for the timers.
Note: this has to be done in main() before any threads are created
so they all inherit the same mask. Doing it later is subject to
race conditions */
sigemptyset(&alarm_sig);
for (i = SIGRTMIN; i <= SIGRTMAX; i++)
sigaddset(&alarm_sig, i);
sigprocmask(SIG_BLOCK, &alarm_sig, NULL);
pthread_create(&t_1, NULL, thread_1, NULL);
sleep(10);
printf("Thread 1 %d iterations\n", thread_1_count);
return 0;
My Problem now, i measured the time with high resolution clock with a period of 20ms.
static void *thread_1(void *arg)
{
struct periodic_info info;
printf("Thread 1 period 10ms\n");
make_periodic(20000, &info);
while (1) {
auto start = std::chrono::high_resolution_clock::now();
printf("Hello\n");
thread_1_count++;
wait_period(&info);
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> ms_double = finish - start;
std::cout << ms_double.count() << "ms\n";
}
return NULL;
}
The output i get ist:
...
19.8556ms
19.8587ms
19.8556ms
19.8543ms
19.8562ms
19.8809ms
19.7592ms
19.8381ms
19.8302ms
19.8437ms
...
So my Question, why is the Time shorter than my Period time, what am i doing wrong ?
To be more accurate, don't take time twice on each iteration, keep the last value, like this:
static void *thread_1(void *arg)
{
struct periodic_info info;
printf("Thread 1 period 10ms\n");
make_periodic(20000, &info);
auto start = std::chrono::high_resolution_clock::now();
while (1) {
wait_period(&info);
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> ms_double = finish - start;
std::cout << ms_double.count() << "ms\n";
start = finish;
}
return NULL;
}
This way, it will make the time measuring more accurate.
Need some help with PTHREADS. I want to keep over 1000 threads opened at any time, something like a thread pool. Here is the code :
/*
gcc -o test2 test2.cpp -static -lpthread -lstdc++
*/
#include <iostream>
#include <cstdlib>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstring>
#include <stdexcept>
#include <cstdlib>
int NUM_THREADS = 2000;
int MAX_THREADS = 100;
int THREADSTACK = 65536;
struct thread_struct{
int arg1;
int arg2;
};
pthread_mutex_t mutex_;
static unsigned int thread_count = 0;
string exec(const char* cmd)
{
int DEBUG=0;
char buffer[5000];
string result = "";
FILE* pipe = popen(cmd, "r");
if (!pipe && DEBUG) throw runtime_error("popen() failed!");
try
{
while (!feof(pipe))
{
if (fgets(buffer, 128, pipe) != NULL)
{
result += buffer;
}
}
}
catch(...)
{
pclose(pipe);
throw;
}
pclose(pipe);
return result;
}
void *thread_test(void *arguments)
{
pthread_mutex_lock(&mutex_);
thread_count++;
pthread_mutex_unlock(&mutex_);
// long tid;
// tid = (long)threadid;
struct thread_struct *args = (thread_struct*)arguments;
/*
printf("ARG1=%d\n",args->arg1);
printf("ARG2=%d\n",args->arg2);
*/
int thread_id = (int) args->arg1;
/*
int random_sleep;
random_sleep = rand() % 10 + 1;
printf ("RAND=[%d]\n", random_sleep);
sleep(random_sleep);
*/
int random_sleep;
random_sleep = rand() % 10 + 5;
// printf ("RAND=[%d]\n", random_sleep);
char command[100];
memset(command,0,sizeof(command));
sprintf(command,"sleep %d",random_sleep);
exec(command);
random_sleep = rand() % 100000 + 500000;
usleep(random_sleep);
// simulation of a work between 5 and 10 seconds
// sleep(random_sleep);
// printf("#%d -> sleep=%d total_threads=%u\n",thread_id,random_sleep,thread_count);
pthread_mutex_lock(&mutex_);
thread_count--;
pthread_mutex_unlock(&mutex_);
pthread_exit(NULL);
}
int main()
{
// pthread_t threads[NUM_THREADS];
int rc;
int i;
usleep(10000);
srand ((unsigned)time(NULL));
unsigned int thread_count_now = 0;
pthread_attr_t attrs;
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, THREADSTACK);
pthread_mutex_init(&mutex_, NULL);
for( i=0; i < NUM_THREADS; i++ )
{
create_thread:
pthread_mutex_lock(&mutex_);
thread_count_now = thread_count;
pthread_mutex_unlock(&mutex_);
// printf("thread_count in for = [%d]\n",thread_count_now);
if(thread_count_now < MAX_THREADS)
{
printf("CREATE thread [%d]\n",i);
struct thread_struct struct1;
struct1.arg1 = i;
struct1.arg2 = 999;
pthread_t temp_thread;
rc = pthread_create(&temp_thread, NULL, &thread_test, (void *)&struct1);
if (rc)
{
printf("Unable to create thread %d\n",rc);
sleep(1);
pthread_detach(temp_thread);
goto create_thread;
}
}
else
{
printf("Thread POOL full %d of %d\n",thread_count_now,MAX_THREADS);
sleep(1);
goto create_thread;
}
}
pthread_attr_destroy(&attrs);
pthread_mutex_destroy(&mutex_);
// pthread_attr_destroy(&attrs);
printf("Proccess completed!\n");
pthread_exit(NULL);
return 1;
}
After spawning 300 threads it begins to give
errors, return code from pthread_create() is 11, and after that keeps executing them one by one.
What im i doing wrong?
According to this website, error code 11 corresponds to EAGAIN which means according to this:
Insufficient resources to create another thread.
A system-imposed limit on the number of threads was encountered.
Hence to solve your problem either create less threads or wait for running ones to finish before creating new ones.
You can also change default thread stack size see pthread_attr_setstacksize
I am trying to create an application which create one main thread and 10 slave threads. I want to run the slave threads once after the main thread is run. So for each main thread execution, each slave thread is going to execute once. I tried to handle this with two different conditional variables. So, one is used for slave threads so they can wait until the main thread notify them and another conditional variable for the main thread which is signaled after each child finish its task, so the main thread can check if all the slave threads are done or not. The code is as follows:
// STD
#include <iostream>
#include <vector>
// BOOST
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
std::vector<boost::thread*> threads;
std::vector<boost::mutex*> data_ready_mutex;
std::vector<boost::condition_variable*> cond;
std::vector<bool> data_ready;
std::vector<int> num_run;
boost::mutex check_finish_mutex;
std::vector<bool> finished;
boost::atomic<int> data;
boost::atomic<int> next_thread_id;
boost::mutex finished_task_mutex;
boost::condition_variable finished_task_cond;
bool finished_task = false;
void signal_finished(const int& id)
{
{
boost::lock_guard<boost::mutex> lock(finished_task_mutex);
finished[id] = true;
finished_task = true;
}
finished_task_cond.notify_all();
}
void signal_slave(const int& id)
{
{
boost::lock_guard<boost::mutex> lock(*data_ready_mutex[id]);
data_ready[id] = true;
}
cond[id]->notify_all();
}
void slave_therad()
{
int id = next_thread_id++;
std::cout << "( " << id << " ) slave_thread created\n";
while (true)
{
boost::unique_lock<boost::mutex> lock(*data_ready_mutex[id]);
while (!data_ready[id])
{
cond[id]->wait(lock);
}
finished[id] = false;
data_ready[id] = false;
data++;
num_run[id]++;
signal_finished(id);
}
}
void main()
{
size_t nThreads = 10;
data_ready_mutex.resize(nThreads);
cond.resize(nThreads);
data_ready.resize(nThreads);
finished.resize(nThreads);
num_run.resize(nThreads, 0);
for (size_t i = 0; i < nThreads; i++)
{
data_ready_mutex[i] = new boost::mutex();
cond[i] = new boost::condition_variable();
data_ready[i] = false;
finished[i] = false;
}
for (size_t i = 0; i < nThreads; i++)
{
threads.push_back(new boost::thread(slave_therad));
}
while (true)
{
clock_t start_time = clock();
for (size_t i = 0; i < threads.size(); i++)
signal_slave(static_cast<int>(i));
while (true)
{
boost::unique_lock<boost::mutex> lock(finished_task_mutex);
while (!finished_task)
{
finished_task_cond.wait(lock);
}
finished_task = false;
size_t i = 0;
for (; i < finished.size(); i++)
{
if (!finished[i]) break;
}
if (i == finished.size()) break;
}
clock_t end_time = clock();
std::cout << "Elapsed Time = " << static_cast<float>(end_time - start_time) / CLOCKS_PER_SEC << std::endl;
for (size_t i = 0; i < threads.size(); i++)
finished[i] = false;
}
for (size_t i = 0; i < nThreads; i++)
{
threads[i]->join();
}
}
The problem is that somewhere the code stops and it stuck in deadlock.
Also, I tried to change the way to implement. So, I used an atomic<int> which counts the number of threads which has finished their task and in the main thread I check if the number of threads is equal to number of threads which has updated themselves but this method also stuck somewhere and goes into deadlock.
The code can be found here:
// STD
#include <iostream>
#include <vector>
// BOOST
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
std::vector<boost::thread*> threads; //!< Slave Threads array
std::vector<boost::mutex*> data_ready_mutex; //!< Mutex to guard the data_ready
std::vector<bool> data_ready; //!< Shows if the data is ready for the slave thread or not.
std::vector<boost::condition_variable*> cond; //!< conditional variable to wait on data being ready for the slave thread.
std::vector<int> num_run; //!< Stores the number of times each slave thread is run.
boost::atomic<int> data; //!< Stores the data processed by each slave thread
boost::atomic<int> next_thread_id; //!< id for the next thread (used for giving an id from 0,..., nThreads-1
boost::atomic<int> num_threads_done; //!< Stores the number of slave threads which has finished their task
//! Signals a slave thread to start its task
void signal_slave(const int& id)
{
{
boost::lock_guard<boost::mutex> lock(*data_ready_mutex[id]);
data_ready[id] = true;
}
cond[id]->notify_all();
}
//! Slave thread function
void slave_therad()
{
// assign an id to the current slave_thread
int id = next_thread_id++;
std::cout << "( " << id << " ) slave_thread created\n";
while (true)
{
// wait for a signal from the main thread
boost::unique_lock<boost::mutex> lock(*data_ready_mutex[id]);
while (!data_ready[id])
{
cond[id]->wait(lock);
}
// make the data not ready, so the loop is not going to run without the main thread signal after the thread is done.
data_ready[id] = false;
// TASK for SLAVE THREAD
data++;
// Increase the number of times the thread is run
num_run[id]++;
// Increase the number of threads which has finished their tasks.
num_threads_done++;
}
}
void main()
{
size_t nThreads = 10;
// creating the data ready mutexes, conditional variables, data_ready variable (bools), num_runs array.
data_ready_mutex.resize(nThreads);
cond.resize(nThreads);
data_ready.resize(nThreads);
num_run.resize(nThreads, 0);
for (size_t i = 0; i < nThreads; i++)
{
data_ready_mutex[i] = new boost::mutex();
cond[i] = new boost::condition_variable();
data_ready[i] = false;
}
// Creating the slave threads
for (size_t i = 0; i < nThreads; i++)
{
threads.push_back(new boost::thread(slave_therad));
}
// Main Thread Body
while (true)
{
clock_t start_time = clock();
// Reset the number of threads which are done.
num_threads_done = 0;
// Signals the slave threads to start doing their task.
for (size_t i = 0; i < threads.size(); i++)
signal_slave(static_cast<int>(i));
// Wait until all the slave threads are done.
while (true)
if (num_threads_done == threads.size()) break;
clock_t end_time = clock();
std::cout << "Elapsed Time = " << static_cast<float>(end_time - start_time) / CLOCKS_PER_SEC << std::endl;
}
for (size_t i = 0; i < nThreads; i++)
{
threads[i]->join();
}
}
Even, I tried to fix the issue with barriers but it did not fix my problem. the code is as follows:
// STD
#include <iostream>
#include <vector>
// BOOST
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
boost::barrier* barrier; //!< barrier to make sure all the slave threads are done their tasks.
std::vector<boost::thread*> threads;
std::vector<boost::mutex*> data_ready_mutex; //!< Mutex to guard the data_ready
std::vector<bool> data_ready; //!< Shows if the data is ready for the slave thread or not.
std::vector<boost::condition_variable*> cond; //!< conditional variable to wait on data being ready for the slave thread.
std::vector<int> num_run; //!< Stores the number of times each slave thread is run.
boost::atomic<int> data; //!< Stores the data processed by each slave thread
boost::atomic<int> next_thread_id; //!< id for the next thread (used for giving an id from 0,..., nThreads-1
boost::atomic<int> num_threads_done; //!< Stores the number of slave threads which has finished their task
std::vector<bool> finished; //!< Array which stores if all the slave threads are done or not.
boost::mutex finished_task_mutex; //!< mutex to guard the finished_task variable
boost::condition_variable finished_task_cond; //!< Conditional variable to wait for all the threads to finish they tasks.
boost::atomic<bool> finished_task(false); //!< Variable which stores if the task of slave_threads are finished or not.
void signal_finished(const int& id)
{
{
boost::lock_guard<boost::mutex> lock(finished_task_mutex);
finished[id] = true;
finished_task = true;
}
finished_task_cond.notify_all();
}
void signal_slave(const int& id)
{
{
boost::lock_guard<boost::mutex> lock(*data_ready_mutex[id]);
data_ready[id] = true;
}
cond[id]->notify_all();
}
void slave_therad()
{
int id = next_thread_id++;
std::cout << "( " << id << " ) slave_thread created\n";
while (true)
{
boost::unique_lock<boost::mutex> lock(*data_ready_mutex[id]);
while (!data_ready[id])
{
cond[id]->wait(lock);
}
finished[id] = false;
data_ready[id] = false;
data++;
num_run[id]++;
barrier->wait();
signal_finished(id);
}
}
void main()
{
size_t nThreads = 10;
data_ready_mutex.resize(nThreads);
cond.resize(nThreads);
data_ready.resize(nThreads);
finished.resize(nThreads);
num_run.resize(nThreads, 0);
for (size_t i = 0; i < nThreads; i++)
{
data_ready_mutex[i] = new boost::mutex();
cond[i] = new boost::condition_variable();
data_ready[i] = false;
finished[i] = false;
}
barrier = new boost::barrier(nThreads);
for (size_t i = 0; i < nThreads; i++)
{
threads.push_back(new boost::thread(slave_therad));
}
while (true)
{
clock_t start_time = clock();
for (size_t i = 0; i < threads.size(); i++)
signal_slave(static_cast<int>(i));
while (true)
{
boost::unique_lock<boost::mutex> lock(finished_task_mutex);
while (!finished_task)
{
finished_task_cond.wait(lock);
}
finished_task = false;
break;
}
clock_t end_time = clock();
std::cout << "Elapsed Time = " << static_cast<float>(end_time - start_time) / CLOCKS_PER_SEC << std::endl;
for (size_t i = 0; i < threads.size(); i++)
finished[i] = false;
}
for (size_t i = 0; i < nThreads; i++)
{
threads[i]->join();
}
}
[UPDATED]
So, I simply used the mutex, conditional variables and data_ready in a struct as follows and now the code is working. I think there was a bug with using pointer to mutex and so on. the code is as follows:
//#define SYNC_WITH_BARRIER
#define SYNC_WITH_ATOMICS
// STD
#include <iostream>
#include <vector>
// BOOST
#include <boost/thread.hpp>
#include <boost/atomic.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
std::vector<boost::thread*> threads;
boost::atomic<int> next_thread_id(0);
boost::mutex finished_task_mutex;
boost::condition_variable finished_task_cond;
bool finished_task = false;
boost::atomic<int> num_finished_tasks(0);
struct Work
{
Work(boost::barrier& _barrier) : b(&_barrier)
{
}
boost::barrier* b;
boost::mutex data_ready_mutex;
boost::condition_variable data_ready_cond;
bool data_ready;
int num_run;
boost::atomic<int> data;
bool finished;
void signal_slave()
{
{
boost::lock_guard<boost::mutex> lock(data_ready_mutex);
data_ready = true;
data_ready_cond.notify_all();
}
}
void slave_therad()
{
int id = next_thread_id++;
std::cout << "( " << id << " ) slave_thread created\n";
while (true)
{
boost::unique_lock<boost::mutex> lock(data_ready_mutex);
while (!data_ready)
{
data_ready_cond.wait(lock);
}
finished = false;
data_ready = false;
data++;
num_run++;
#ifdef SYNC_WITH_BARRIER
b->count_down_and_wait();
#else
#ifdef SYNC_WITH_ATOMICS
num_finished_tasks++;
#endif
#endif
}
}
};
#include <boost/chrono.hpp>
#include <boost/chrono/chrono_io.hpp>
using hrc = boost::chrono::high_resolution_clock;
void main()
{
size_t nThreads = 10;
boost::thread_group tg;
boost::ptr_vector<Work> work_items;
work_items.reserve(nThreads);
boost::barrier finish(nThreads + 1); // one for the main thread
for (size_t i = 0; i < nThreads; i++)
{
work_items.push_back(new Work(finish));
tg.create_thread(boost::bind(&Work::slave_therad, boost::ref(work_items.back())));
}
while (true)
{
auto start_time = hrc::now();
num_finished_tasks = 0;
for (size_t i = 0; i < work_items.size(); i++)
work_items[i].signal_slave();
#ifdef SYNC_WITH_BARRIER
finish.count_down_and_wait();
#else
#ifdef SYNC_WITH_ATOMICS
while (true) if (num_finished_tasks == work_items.size()) break;
#endif
#endif
clock_t end_time = clock();
std::cout << "Elapsed Time = " << hrc::now() - start_time << std::endl;
}
for (size_t i = 0; i < nThreads; i++)
{
threads[i]->join();
}
}
#sehe even with barrier, it stuck in deadlock. – mmostajab 5 mins ago
Since youdon't show anything about what you're doing there, let me give you a startup boost by incorporating a large chunk of all the suggestions you received:
Live On Coliru
#include <boost/atomic.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>
namespace /*static*/ {
boost::atomic<int> data;
boost::atomic<int> num_threads_done;
struct Work {
void signal_slave()
{
boost::lock_guard<boost::mutex> lock(data_ready_mutex);
data_ready = true;
cond.notify_all();
}
void slave_thread()
{
static boost::atomic_int _id_gen(0);
id = _id_gen++;
std::cout << "(" << id << ") slave_thread created\n";
while (true) {
boost::unique_lock<boost::mutex> lock(data_ready_mutex);
cond.wait(lock, [&]{ return data_ready; });
data_ready = false;
data++;
num_run++;
num_threads_done++;
}
}
private:
int id = 0;
bool data_ready = false;
int num_run = 0;
boost::mutex data_ready_mutex;
boost::condition_variable cond;
};
}
#include <boost/chrono.hpp>
#include <boost/chrono/chrono_io.hpp>
using hrc = boost::chrono::high_resolution_clock;
int main()
{
boost::thread_group tg;
size_t nThreads = 10;
std::vector<Work> works(nThreads);
for (size_t i = 0; i < nThreads; i++) {
tg.create_thread(boost::bind(&Work::slave_thread, boost::ref(works[i])));
}
while (true) {
auto start_time = hrc::now();
for (auto& w : works)
w.signal_slave();
std::cout << "Elapsed Time = " << (hrc::now()-start_time) << std::endl;
}
tg.join_all();
}
Bear in mind, I don't know what you're trying to achieve here. Adding a barrier I had this in mind: how to use boost barrier
I tried to change the #sehe answer, so it solve exactly the problem which I am looking for and I achieved this code:
#include <boost/atomic.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <vector>
namespace /*static*/ {
boost::atomic<int> data;
boost::barrier* slave_thread_finished_barrier;
boost::mutex slave_thread_finished_mutex;
boost::condition_variable slave_thread_finished_cond;
bool slave_thread_finished = false;
struct Work {
void signal_slave()
{
boost::lock_guard<boost::mutex> lock(data_ready_mutex);
data_ready = true;
cond.notify_all();
}
void slave_thread()
{
static boost::atomic_int _id_gen(0);
id = _id_gen++;
std::cout << "(" << id << ") slave_thread created\n";
while (true) {
boost::unique_lock<boost::mutex> lock(data_ready_mutex);
cond.wait(lock, [&]{ return data_ready; });
data_ready = false;
data++;
num_run++;
slave_thread_finished_barrier->wait();
// signaling the main thread that the slave threads are done.
if (id == 0)
{
boost::lock_guard<boost::mutex> lock(slave_thread_finished_mutex);
slave_thread_finished = true;
slave_thread_finished_cond.notify_one();
}
}
}
private:
int id = 0;
bool data_ready = false;
int num_run = 0;
boost::mutex data_ready_mutex;
boost::condition_variable cond;
};
}
#include <boost/chrono.hpp>
#include <boost/chrono/chrono_io.hpp>
using hrc = boost::chrono::high_resolution_clock;
int main()
{
boost::thread_group tg;
size_t nThreads = 10;
slave_thread_finished_barrier = new boost::barrier(nThreads);
std::vector<Work> works(nThreads);
for (size_t i = 0; i < nThreads; i++) {
tg.create_thread(boost::bind(&Work::slave_thread, boost::ref(works[i])));
}
while (true) {
auto start_time = hrc::now();
for (auto& w : works)
w.signal_slave();
// Wait for slave threads to finish.
boost::unique_lock<boost::mutex> lock(slave_thread_finished_mutex);
slave_thread_finished_cond.wait(lock, [&]{ return slave_thread_finished; });
slave_thread_finished = false;
std::cout << "Elapsed Time = " << (hrc::now() - start_time) << std::endl;
}
tg.join_all();
}
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.