I am making a program using threads and a shared buffer. The two threads run indefinitely in the background, one thread will fill a shared buffer with data and the other thread will write the content of the shared buffer into a file.
The user can start or stop the data filling which is resulting in the thread entering into a waiting state until the user starts the thread again. Each loop the buffer is filled with 50 floats.
This is the code :
#include <iostream>
#include <vector>
#include <iterator>
#include <utility>
#include <fstream>
#include <condition_variable>
#include <mutex>
#include <thread>
using namespace std;
std::mutex m;
std::condition_variable cv;
std::vector<std::vector<float>> datas;
bool keep_running = true, start_running = false;
void writing_thread()
{
ofstream myfile;
bool opn = false;
while(1)
{
while(keep_running)
{
// Open the file only once
if(!opn)
{
myfile.open("IQ_Datas.txt");
opn = true;
}
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {return !datas.empty();});
auto d = std::move(datas);
lk.unlock();
for(auto &entry : d)
{
for(auto &e : entry)
myfile << e << endl;
}
}
if(opn)
{
myfile.close();
opn = false;
}
}
}
void sending_thread()
{
std::vector<float> m_buffer;
int cpt=0;
//Fill the buffer with 50 floats
for(float i=0; i<50; i++)
m_buffer.push_back(i);
while(1)
{
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {return keep_running && start_running;});
}
while(keep_running)
{
//Each loop d is containing 50 floats
std::vector<float> d = m_buffer;
cout << "in3" << endl; //Commenting this line makes the program crash
{
std::lock_guard<std::mutex> lk(m);
if (!keep_running)break;
datas.push_back(std::move(d));
}
cv.notify_one();
cpt++;
}
cout << "Total data: " << cpt*50 << endl;
cpt = 0;
}
}
void start()
{
{
std::unique_lock<std::mutex> lk(m);
start_running = true;
}
cv.notify_all();
}
void stop()
{
{
std::unique_lock<std::mutex> lk(m);
start_running = false;
}
cv.notify_all();
}
int main()
{
int go = 0;
thread t1(sending_thread);
thread t2(writing_thread);
t1.detach();
t2.detach();
while(1)
{
std::cin >> go;
if(go == 1)
{
start();
keep_running = true;
}
else if(go == 0)
{
stop();
keep_running = false;
}
}
return 0;
}
I have 2 issues with this code :
When commenting the line cout << "in3" << endl; the program will crash after ~20-40 seconds with the error message : terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc. If i let the cout, the program will run without problems.
When the program is working, after stoping sending_thread i display the total amount of data that has been copied with cout << "Total data: " << cpt*50 << endl;. For small amount of datas, all of it is written correctly into the file but when the amount is big, there is missing data. Missing/Correct data (Total number of lines in the file does not match total data)
Why with the cout the program is running correctly ? And what is causing the missing data ? Is it because sending_thread is filling the buffer too fast while writing_threadtakes too much time to write into the file?
EDIT: Some precisions, adding more cout into sending_threadseems to fix all the issues. First thread produced 21 million floats and second thread successfully wrote in the file 21 million floats. It seems like without the cout, producer threads works too fast for the consumer thread to keep retrieving data from the shared buffer while writing it into a file.
To avoid:
Moved-from object 'datas' of type 'std::vector' is moved:
auto d = std::move(datas);
^~~~~~~~~~~~~~~~
Replace this:
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {return !datas.empty();});
auto d = std::move(datas);
lk.unlock();
With this:
// Wait until main() sends data
std::vector<std::vector<float>> d;
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] { return !datas.empty(); });
datas.swap(d);
}
Also replace your bool variables that are accessed from multiple threads with std::atomic_bool or std::atomic_flag.
The bad_alloc comes from sending_thread being much faster than writing_thread so it will run out of memory. When you slow down sending_thread enough (with printing), the problem is less visible, but you should have some synchronization to do it properly. You could make a wrapper class around it and provide insert and extraction methods to make sure all access is synchronized properly and also give it a max number of elements. An example:
template<typename T>
class atomic2dvector {
public:
atomic2dvector(size_t max_elements) : m_max_elements(max_elements) {}
atomic2dvector(const atomic2dvector&) = delete;
atomic2dvector(atomic2dvector&&) = delete;
atomic2dvector& operator=(const atomic2dvector&) = delete;
atomic2dvector& operator=(atomic2dvector&&) = delete;
~atomic2dvector() { shutdown(); }
bool insert_one(std::vector<T>&& other) {
std::unique_lock<std::mutex> lock(m_mtx);
while(m_current_elements + m_data.size() > m_max_elements && m_shutdown == false)
m_cv.wait(lock);
if(m_shutdown) return false;
m_current_elements += other.size();
m_data.emplace_back(std::forward<std::vector<T>>(other));
m_cv.notify_one();
return true;
}
std::vector<std::vector<T>> extract_all() {
std::vector<std::vector<T>> return_value;
std::unique_lock<std::mutex> lock(m_mtx);
while(m_data.empty() && m_shutdown == false) m_cv.wait(lock);
if(m_shutdown == false) {
m_current_elements = 0;
return_value.swap(m_data);
} else {
// return an empty vector if we should shutdown
}
m_cv.notify_one();
return return_value;
}
bool is_active() const { return m_shutdown == false; }
void shutdown() {
m_shutdown = true;
m_cv.notify_all();
}
private:
size_t m_max_elements;
size_t m_current_elements = 0;
std::atomic<bool> m_shutdown = false;
std::condition_variable m_cv{};
std::mutex m_mtx{};
std::vector<std::vector<T>> m_data{};
};
If you'd like to keep extracting data even after shutdown, you can change extract_all() to this:
std::vector<std::vector<T>> extract_all() {
std::vector<std::vector<T>> return_value;
std::unique_lock<std::mutex> lock(m_mtx);
while(m_data.empty() && m_shutdown == false) m_cv.wait(lock);
m_current_elements = 0;
return_value.swap(m_data);
m_cv.notify_one();
return return_value;
}
A full example could look like this:
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <fstream>
#include <iostream>
#include <iterator>
#include <mutex>
#include <thread>
#include <utility>
#include <vector>
using namespace std;
template<typename T>
class atomic2dvector {
public:
atomic2dvector(size_t max_elements) : m_max_elements(max_elements) {}
atomic2dvector(const atomic2dvector&) = delete;
atomic2dvector(atomic2dvector&&) = delete;
atomic2dvector& operator=(const atomic2dvector&) = delete;
atomic2dvector& operator=(atomic2dvector&&) = delete;
~atomic2dvector() { shutdown(); }
bool insert_one(std::vector<T>&& other) {
std::unique_lock<std::mutex> lock(m_mtx);
while(m_current_elements + m_data.size() > m_max_elements &&
m_shutdown == false)
m_cv.wait(lock);
if(m_shutdown) return false;
m_current_elements += other.size();
m_data.emplace_back(std::forward<std::vector<T>>(other));
m_cv.notify_one();
return true;
}
std::vector<std::vector<T>> extract_all() {
std::vector<std::vector<T>> return_value;
std::unique_lock<std::mutex> lock(m_mtx);
while(m_data.empty() && m_shutdown == false) m_cv.wait(lock);
m_current_elements = 0;
return_value.swap(m_data);
m_cv.notify_one();
return return_value;
}
bool is_active() const { return m_shutdown == false; }
void shutdown() {
m_shutdown = true;
m_cv.notify_all();
}
private:
size_t m_max_elements;
size_t m_current_elements = 0;
std::atomic<bool> m_shutdown = false;
std::condition_variable m_cv{};
std::mutex m_mtx{};
std::vector<std::vector<T>> m_data{};
};
std::mutex m;
std::condition_variable cv;
atomic2dvector<float> datas(256 * 1024 * 1024 / sizeof(float)); // 0.25 GiB limit
std::atomic_bool start_running = false;
void writing_thread() {
std::ofstream myfile("IQ_Datas.txt");
if(myfile) {
std::cout << "writing_thread waiting\n";
std::vector<std::vector<float>> d;
while((d = datas.extract_all()).empty() == false) {
std::cout << "got " << d.size() << "\n";
for(auto& entry : d) {
for(auto& e : entry) myfile << e << "\n";
}
std::cout << "wrote " << d.size() << "\n\n";
}
}
std::cout << "writing_thread shutting down\n";
}
void sending_thread() {
std::vector<float> m_buffer;
std::uintmax_t cpt = 0;
// Fill the buffer with 50 floats
for(float i = 0; i < 50; i++) m_buffer.push_back(i);
while(true) {
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, [] {
return start_running == true || datas.is_active() == false;
});
}
if(datas.is_active() == false) break;
std::cout << "sending...\n";
while(start_running == true) {
// Each loop d is containing 50 floats
std::vector<float> d = m_buffer;
if(datas.insert_one(std::move(d)) == false) break;
cpt++;
}
cout << "Total data: " << cpt * 50 << endl;
cpt = 0;
}
std::cout << "sending_thread shutting down\n";
}
void start() {
std::unique_lock<std::mutex> lk(m);
start_running = true;
cv.notify_all();
}
void stop() {
std::unique_lock<std::mutex> lk(m);
start_running = false;
cv.notify_all();
}
void quit() {
datas.shutdown();
cv.notify_all();
}
int main() {
int go = 0;
thread t1(sending_thread);
thread t2(writing_thread);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Enter 1 to make the sending thread send and 0 to make it stop "
"sending. Enter a non-integer to shutdown.\n";
while(std::cin >> go) {
if(go == 1) {
start();
} else if(go == 0) {
stop();
}
}
std::cout << "--- shutting down ---\n";
quit();
std::cout << "joining threads\n";
t1.join();
std::cout << "t1 joined\n";
t2.join();
std::cout << "t2 joined\n";
}
I am new to multithreading and i need your help.
Consider the following code:
vector <int> vec;
int j = 0;
void Fill()
{
for (int i = 0; i < 500; i++)
{
Sleep(500);
vec.push_back(i);
}
}
void Proces()
{
int count = 0;
int n=-1;
while (true) {
Sleep(250);
if (!vec.empty())
{
if (n != vec.back()) {
n = vec.back();
cout << n;
count++;
}
}
if (count == 101)break;
}
}
void getinput()
{
while (true) {
int k=0;
cin >> k;
//if the user enters an integer i want to kill all the threads
}
}
int main()
{
thread t1(Fill);
thread t2(Proces);
thread t3(getinput);
t1.join();
t2.join();
t3.join();
cout << "From main()";
}
The point is that i want to kill t1(Fill) and t2(Proces) from t3(getinput).Is there and way to do it,and if there is could you please post and example.
A common way to make a thread exit is to have an (atomic) flag that the thread checks to see if it should exit. Then externally you set this flag and the thread will notice it and exit naturally.
Something like
#include <thread>
#include <atomic>
#include <iostream>
#include <chrono>
// Flag telling the thread to continue or exit
std::atomic<bool> exit_thread_flag{false};
void thread_function()
{
// Loop while flag if not set
while (!exit_thread_flag)
{
std::cout << "Hello from thread\n";
std::this_thread::sleep_for(std::chrono::seconds(1)); // Sleep for one second
}
}
int main()
{
std::thread t{thread_function}; // Create and start the thread
std::this_thread::sleep_for(std::chrono::seconds(5)); // Sleep for five seconds
exit_thread_flag = true; // Tell thread to exit
t.join(); // Wait for thread to exit
}
You have to define an exit condition and lock the container before accessing it. Of course you could build an own collection as wrapper around an existing using proper locking and thus making it thread-safe.
Here is an example of locking and an exit condition:
class Test
{
public:
Test()
: exitCondition(false)
{
work = std::thread([this]() { DoWork(); });
}
~Test()
{
if (work.joinable())
work.join();
}
void Add(int i)
{
mutex.lock();
things.push_back(i);
mutex.unlock();
}
void RequestStop(bool waitForExit = false)
{
exitCondition.exchange(true);
if (waitForExit)
work.join();
}
private:
void DoWork()
{
while (!exitCondition)
{
mutex.lock();
if (!things.empty())
{
for (auto itr = things.begin(); itr != things.end();)
itr = things.erase(itr);
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
mutex.unlock();
}
}
private:
std::vector<int> things;
std::thread work;
std::atomic<bool> exitCondition;
std::mutex mutex;
};
int wmain(int, wchar_t**)
{
Test t;
t.Add(1);
t.Add(2);
t.Add(3);
t.RequestStop(true);
return 0;
}
std::atomic<bool> exit_flag{false};
...
void Fill() {
for (int i = 0; i < 500; i++) {
if (exit_flag) return;
...
}
}
void Proces() {
while (true) {
if (exit_flag) return;
...
}
}
void getinput() {
while (true) {
...
if ( /* the user enters an integer i want to kill all the threads */ )
exit_flag = true;
}
}
I wrote the following structure to implement a simple single producer / multi consumer synchronization. I'm using two integers available_index and consumed_index, access to consumed_index is protected by the condition variable cv. Here's the code:
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <thread>
struct ParserSync {
std::mutex worker_lock;
std::condition_variable cv;
int consumed_index = -1;
int available_index = -1;
bool exit_flag = false;
int consume_index() {
int ret = -1;
// get worker_lock
std::unique_lock<std::mutex> w_lock(worker_lock);
// wait for exit_flag or new available index
cv.wait(w_lock, [this] { return exit_flag || available_index > consumed_index; });
if (available_index > consumed_index) {
consumed_index++;
ret = consumed_index;
}
// Unlock mutex and notify another thread
w_lock.unlock();
cv.notify_one();
return ret;
}
void publish_index() {
available_index++;
std::cout << "before" << std::endl;
cv.notify_one();
std::cout << "after" << std::endl;
}
void set_exit() {
exit_flag = true;
cv.notify_all();
}
};
I tested my implementation using the following code (just a simple example to show the problem):
void producer(ParserSync &ps){
for (int i=0;i<5000;i++){
ps.publish_index();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ps.set_exit();
std::cout << "Producer finished!" << std::endl;
}
void consumer(ParserSync &ps){
while (true){
int idx = ps.consume_index();
if (idx == -1)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(4));
}
std::cout << "Consumer finished!" << std::endl;
}
int main() {
ParserSync ps{};
const int num_consumers = 4;
std::vector<std::thread> consumer_threads(num_consumers);
// start consumers
for (int i = 0; i < num_consumers; ++i) {
consumer_threads[i] = std::thread{consumer, std::ref(ps)};
}
// start producer
std::thread producer_thread = std::thread{producer, std::ref(ps)};
for (int i = 0; i < num_consumers; ++i) {
consumer_threads[i].join();
}
producer_thread.join();
std::cout << "Program finished" << std::endl;
return 0;
}
I would expect that producer thread produces 5000 indices and exits afterwards, but unfortunately, it gets stuck at some random iteration. I used print statements to find the code line that blocks and tracked it down to cv.notify_one();. This is the (shortened) console output:
...
before
after
before
after
before
Does anyone know why the call to cv.notify_one(); blocks?
I'm using MinGW (x86_64-6.2.0-posix-seh-rt_v5-rev1) on Windows 10.
Thanks in advance!
EDIT:
When compiling the exact same code with Visual Studio, the program works as expected and doesn't lock itself up. Unfortunately, I need to use MinGW for other reasons.
I wrote a code to implement spin lock and mutex lock.
There is an interesting but. A magic cout can keep my program alive. If I remove the cout, my program will be sleeping forever. (This only happens in Linux. Windows is doing fine)
Any one have a clue?
#include <pthread.h>
#include <iostream>
#include <queue>
#include <sys/time.h>
#include <stdexcept>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define Tcount 10
#define TheLock MutexLock
static inline int TAS(volatile int * ptr) {
unsigned long result;
asm volatile("lock;"
"xchgl %0, %1;"
: "=r"(result), "=m"(*ptr)
: "0"(1), "m"(*ptr)
: "memory");
return result;
}
class SpinLock {
private:
int lock;
pthread_t owner;
public:
SpinLock() {
lock = 0;
}
void getLock() {
while (TAS(&lock) == 1) {
}
owner = pthread_self();
}
void releaseLock() {
if (lock == 0) {
cout << "Spin no lock" << endl;
return;
} else if (owner == pthread_self()) {
owner = NULL;
lock = 0;
} else {
throw runtime_error("Spin can't release");
}
}
};
class MutexLock {
private:
int lock;
pthread_t owner;
queue<pthread_t> q;
SpinLock qLock;
public:
MutexLock() {
lock = 0;
}
void getLock(int id) {
pthread_t self = pthread_self();
cout<<"a"<<endl;// magic cout
if (TAS(&lock) == 0) {
owner = self;
return;
}
qLock.getLock();
q.push(self);
qLock.releaseLock();
while (owner != self) {
}
}
void releaseLock(int id) {
if (lock == 0) {
cout << "Mutex no lock" << endl;
return;
} else if (owner == pthread_self()) {
qLock.getLock();
if (q.empty()) {
owner = NULL;
lock = 0;
} else {
owner = q.front();
q.pop();
}
qLock.releaseLock();
} else {
throw runtime_error("Mutex can't release");
}
}
};
TheLock lock;
int g = 0;
void* run(void* pt) {
int id = (int) pt;
for (int i = 0; i < 10000; i++) {
lock.getLock(id);
//cout<<"Thread "<<id<<" get lock, g="<<g<<endl;
int next = g + 1;
g = next;
//cout<<"Thread "<<id<<" release lock, g="<<g<<endl;
lock.releaseLock(id);
}
return NULL;
}
int main() {
pthread_t th[Tcount];
long mtime, seconds, useconds;
struct timeval start, end;
gettimeofday(&start, NULL);
for (int i = 0; i < Tcount; i++) {
pthread_create(&th[i], NULL, run, (void*) (i+10));
}
for (int i = 0; i < Tcount; i++) {
pthread_join(th[i], 0);
}
gettimeofday(&end, NULL);
seconds = end.tv_sec - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
mtime = ((seconds) * 1000000 + useconds);
cout << "g=" << g << endl;
cout << "time=" << mtime << endl;
return 0;
}
You cannot implement a mutex by using the volatile keyword as the operations may not be atomic. This means that the OS might switch to a different thread before the operation has completed.
For mutex you have to use the OS. It is the only thing that knows when threads are being switched.
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.