Suppose to have a thread like this
void mythread()
{
int res;
while(1) {
{
boost::lock_guard<boost::mutex> lock(mylock);
res = do_my_stuff();
}
boost::this_thread::sleep(boost::posix_time::seconds(5));
}
}
and that the thread is currently sleeping. If something happens outside of the thread, I'd like to be able to increase the sleep time.
What is the best way to do it?
Using a condition_variable to signal changes to the deadline
This has the benefit of supporting scenarios where the timeout is shortened:
See it Live On Coliru
#include <thread>
#include <chrono>
#include <iostream>
#include <condition_variable>
namespace demo
{
namespace chrono = std::chrono;
using our_clock = chrono::system_clock;
struct Worker
{
mutable std::mutex _mx;
// shared, protected by _mx:
our_clock::time_point _deadline;
mutable std::condition_variable _cv;
Worker(our_clock::time_point deadline) : _deadline(deadline) {}
void operator()() const {
std::unique_lock<std::mutex> lk(_mx);
_cv.wait_until(lk, _deadline, [this]
{
std::cout << "worker: Signaled\n";
auto now = our_clock::now();
if (now >= _deadline)
return true;
std::cout << "worker: Still waiting " << chrono::duration_cast<chrono::milliseconds>(_deadline - now).count() << "ms...\n";
return false;
});
std::cout << "worker: Done\n";
}
};
}
int main()
{
using namespace demo;
Worker worker(our_clock::now() + chrono::seconds(2));
auto th = std::thread(std::cref(worker));
// after 2 seconds, update the timepoint
std::this_thread::sleep_for(chrono::seconds(1));
{
std::lock_guard<std::mutex> lk(worker._mx);
std::cout << "Updating shared delay value..." << "\n";
worker._deadline = our_clock::now() + chrono::seconds(1);
worker._cv.notify_all();
}
th.join();
}
C++11 standard library (no signaling)
Here's a standard-library only approach which uses no synchronization around the deadline.
I'd have preferred to use atomic time_point for the deadline value itself, but that's not supported. Next best thing would have been shared_ptr<time_point> (with std::atomic_load/atomic_store) but my compiler's library doesn't implement this yet (grrr).
So, instead, I share the 'offset' since a start time:
#include <thread>
#include <chrono>
#include <iostream>
#include <atomic>
namespace demo
{
namespace chrono = std::chrono;
using our_clock = chrono::system_clock;
using shared_delay = std::atomic<our_clock::duration>;
void worker(our_clock::time_point const start, shared_delay const& delay)
{
for (our_clock::time_point deadline; our_clock::now() < (deadline = start + delay.load());)
{
std::cout << "worker: Sleeping for " << chrono::duration_cast<chrono::milliseconds>(deadline - our_clock::now()).count() << "ms...\n";
std::this_thread::sleep_until(deadline);
}
std::cout << "worker: Done\n";
}
}
int main()
{
using namespace demo;
shared_delay delay(chrono::seconds(2));
auto th = std::thread(worker, our_clock::now(), std::cref(delay));
// after 2 seconds, update the timepoint
std::this_thread::sleep_for(chrono::seconds(1));
std::cout << "Updating shared delay value..." << "\n";
delay.store(chrono::seconds(3));
th.join();
}
See it Live on Coliru
Here is a quick and dirty method:
volatile bool someCondition = false;
void callFromOtherThread(bool x) {
boost::lock_guard<boost::mutex> lock(mylock2);
someCondition = x;
}
void mythread()
{
int res;
while(1) {
bool keepwaiting = false;
{
boost::lock_guard<boost::mutex> lock(mylock2);
keepwaiting = someCondition;
}
if (!keepwaiting) {
boost::lock_guard<boost::mutex> lock(mylock);
res = do_my_stuff();
}
boost::this_thread::sleep(boost::posix_time::seconds(5));
}
}
When your thread finished sleeping, it checks it 'something else' happened, and if it did, it skips 'do_my_stuff()' and goes back to sleep again.
I suspect with somewhat more information about your use case it might be possible to rewrite things to use a condition variable.
Related
In C++ how i can write two parallel threads which will work one by one.For example in below code it need to print 0 t 100 sequentially.In below code the numbers are printing ,but all are not sequential.I need to print like 1,2,3,4,5,6.....99.If any body know , try to add with sample code also.
#pragma once
#include <mutex>
#include <iostream>
#include <vector>
#include <thread>
#include <condition_variable>
using namespace std;
class CuncurrentThread
{
public:
mutex mtx;
condition_variable cv;
static bool ready;
static bool processed;
void procThread1()
{
for (int i = 0; i < 100; i += 2)
{
unique_lock<mutex> lk(mtx);
cv.notify_one();
if(lk.owns_lock())
cv.wait(lk);
cout << "procThread1 : " << i << "\n";
lk.unlock();
cv.notify_one();
}
};
void procThread2()
{
for (int i = 1; i < 100; i += 2)
{
unique_lock<mutex> lk(mtx);
cv.notify_one();
if (lk.owns_lock())
cv.wait(lk);
cout << "procThread2 : " << i << "\n";
lk.unlock();
cv.notify_one();
}
};
static void ThreadDriver(CuncurrentThread* thr)
{
vector<thread> threads;
threads.push_back(thread(&CuncurrentThread::procThread1, thr));
threads.push_back(thread(&CuncurrentThread::procThread2, thr));
for (auto& thread : threads)
thread.join();
};
};
bool CuncurrentThread::ready = false;
int main()
{
CuncurrentThread tr;
CuncurrentThread::ThreadDriver(&tr);
}
Assuming you have a valid use case for using two threads like this, here is an example. I prefer using std::async over std::thread it has better abstraction and information exchange with the main thread.
The example is written for 2 threads but can easily be changed to more threads.
Live demo here : https://onlinegdb.com/eQex9o_nMz
#include <future>
#include <condition_variable>
#include <iostream>
// Setup a helper class that sets up
// the three things needed to correctly
// use a condition variable
// 1) a mutex
// 2) a variable
// 3) a condition_variable (which is more of a signal then a variable)
//
// also give this class some functions
// so the the code becomes more self-explaining
class thread_switcher_t
{
public:
void thread1_wait_for_turn()
{
std::unique_lock<std::mutex> lock{ m_mtx };
m_cv.wait(lock, [&] {return (thread_number==0); });
}
void thread2_wait_for_turn()
{
std::unique_lock<std::mutex> lock{ m_mtx };
m_cv.wait(lock, [&] {return (thread_number==1); });
}
void next_thread()
{
std::unique_lock<std::mutex> lock{ m_mtx };
thread_number = (thread_number + 1) % 2;
m_cv.notify_all();
}
private:
std::size_t thread_number{ 0 };
std::mutex m_mtx;
std::condition_variable m_cv;
};
int main()
{
thread_switcher_t switcher;
auto future1 = std::async(std::launch::async, [&]
{
for(std::size_t n = 0; n <= 100; n+=2)
{
switcher.thread1_wait_for_turn();
std::cout << "thread 1 : " << n << "\n";
switcher.next_thread();
}
});
auto future2 = std::async(std::launch::async, [&]
{
for (std::size_t n = 1; n <= 100; n += 2)
{
switcher.thread2_wait_for_turn();
std::cout << "thread 2 : " << n << "\n";
switcher.next_thread();
}
});
future1.get();
future2.get();
return 0;
}
You can use ready variable as a condition for condition variable.
#include <mutex>
#include <iostream>
#include <vector>
#include <thread>
#include <condition_variable>
using namespace std;
class CuncurrentThread
{
public:
mutex mtx;
condition_variable cv;
static bool ready;
//static bool processed;
void procThread1()
{
for (int i = 0; i < 100; i += 2)
{
unique_lock<mutex> lk(mtx);
// cv.notify_one();
// if(lk.owns_lock())
// wait until this condition is true i.e. until ready is false
cv.wait(lk, [&]() { return !ready; });
cout << "procThread1 : " << i << "\n";
// set ready to true and notify waiting thread
ready = true;
lk.unlock();
cv.notify_one();
}
};
void procThread2()
{
for (int i = 1; i < 100; i += 2)
{
unique_lock<mutex> lk(mtx);
// cv.notify_one();
// if (lk.owns_lock())
// wait until this condition is true i.e. until ready is true
cv.wait(lk, [&]() { return ready; });
cout << "procThread2 : " << i << "\n";
// set ready to false and notify waiting thread
ready = false;
lk.unlock();
cv.notify_one();
}
};
static void ThreadDriver(CuncurrentThread* thr)
{
vector<thread> threads;
threads.push_back(thread(&CuncurrentThread::procThread1, thr));
threads.push_back(thread(&CuncurrentThread::procThread2, thr));
for (auto& thread : threads)
thread.join();
};
};
bool CuncurrentThread::ready = false;
int main()
{
CuncurrentThread tr;
CuncurrentThread::ThreadDriver(&tr);
}
Link where I tested this: https://godbolt.org/z/4jEns16oq
Here is some code which increments several chronometers in parallel:
main.cpp
using namespace std;
#include <stdio.h>
#include <time.h>
#include <iostream>
#include <math.h>
#include <cstdlib>
#include <unistd.h>
#include <iostream>
#include <sstream>
#include <thread>
#include <vector>
#include <future>
#include "mychrono.hpp"
int main()
{
std::vector<Chronometer*> car_crono;
Chronometer chrono, output_chrono;
std::vector<std::thread> threads;
std::vector<std::future<Chronometer&>> futures;
std::thread th;
//future<Chronometer> ft;
for(int i = 0; i < 2; i++)
{
car_crono.push_back(new Chronometer);
}
while (1) {
for(int i = 0; i<2; i++)
{
//
// //threads.push_back(std::thread(&Chronometer::start_chrono, car_crono[i], std::ref(chrono)));
// auto ft = std::async(std::launch::async, &Chronometer::start_chrono, car_crono[i], std::ref(chrono));
//
// std::cout << "Hello-world" << std::endl;
futures.emplace_back(std::async(std::launch::async, &Chronometer::start_chrono, car_crono[i], std::ref(chrono)));
}
std::cout << "hello-world" << std::endl;
//auto ft = std::async(std::launch::async, &Chronometer::start_chrono, car_crono[0], std::ref(chrono));
//std::cout << "Hello-world-2" << std::endl;
for(auto&& f: futures){
std::cout << f.get() << '\n';
}
}
car_crono.clear();
}
mychrono.cpp
#include "mychrono.hpp"
#include <time.h>
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sstream>
#include <thread>
//int Chronometer::hour(0), min(0), sec(0);
Chronometer::Chronometer() : hour(0), min(0), sec(0)
{
}
Chronometer& Chronometer::start_chrono(Chronometer& chrono)
{
// if(chrono.hour == 0 && chrono.min == 0 && chrono.sec == 0)
// {
bool condition = true;
while(condition) {
sleep(1);
chrono.sec++;
if(chrono.sec > 59) {
chrono.min++;
chrono.sec = 0;
}
if(chrono.min > 59) {
chrono.hour++;
chrono.sec = 0;
chrono.min = 0;
}
// if(chrono.sec == 10)
// {
// condition = false;
// }
std::cout << "chrono: " << chrono << std::endl;
}
return chrono;
//}
}
Chronometer& Chronometer::finish_chrono(Chronometer& chrono)
{
chrono.hour = 0;
chrono.sec = 0;
chrono.min = 0;
return chrono;
}
std::ostream& operator<<(std::ostream& flux, Chronometer t)
{
flux << t.hour << ":" << t.min << ":" << t.sec;
return flux;
}
Chronometer& Chronometer::operator=(const Chronometer& other)
{
// Guard self assignment
//if (this == &other)
return *this;
}
Chronometer::~Chronometer(){}
mychrono.hpp
#include <time.h>
#include <iostream>
#include <sstream>
#ifndef mychrono_hpp
#define mychrono_hpp
class Chronometer
{
private:
int hour, min, sec;
//std::stringstream ss;
//Chronometer chrono;
public:
Chronometer();
Chronometer& start_chrono(Chronometer& chrono);
Chronometer& finish_chrono(Chronometer& chrono);
friend std::ostream& operator<<(std::ostream& flux, Chronometer t);
Chronometer& operator=(const Chronometer& other);
~Chronometer();
};
#endif
My program runs well my two chronometers in parallel each other but still dependant of my while loop. For example here I will print "hello-world" once but need to wait my threads stop to print a second "hello-world" message in my while loop.
My question is how to make my threads runs in parallel an be completely independant of others instructions in my while loop ?
Tzig had a similar idea as mine, that is using condition variables and such.
I've made a full working example including comments and hopefully written for readability.
#include <chrono>
#include <iostream>
#include <iomanip>
#include <mutex>
#include <future>
#include <condition_variable>
//-----------------------------------------------------------------------------------------------------
// state of the timer.
enum class State
{
idle,
starting,
running,
stopping,
stopped
};
//-----------------------------------------------------------------------------------------------------
// helper class for use of std::condition_variable, makes code more readable
// takes into account the pitfalls of condition variables :
// https://www.modernescpp.com/index.php/c-core-guidelines-be-aware-of-the-traps-of-condition-variables
template<typename T>
class StateVariable
{
public:
StateVariable() = delete;
StateVariable(const StateVariable&) = delete;
StateVariable(StateVariable&&) = delete;
StateVariable& operator=(const StateVariable&) = delete;
explicit StateVariable(const T& value) :
m_value{ value }
{
}
void operator=(const T& value) noexcept
{
{
std::unique_lock<std::mutex> lock(m_value_mutex);
m_value = value;
}
m_value_changed.notify_all();
}
// atomic check and set
T set_if(const T& from_value, const T& to_value) noexcept
{
{
std::unique_lock<std::mutex> lock(m_value_mutex);
if (m_value != from_value) return from_value;
m_value = to_value;
}
m_value_changed.notify_all();
return to_value;
}
const bool try_wait_for(const T& value, const std::chrono::steady_clock::duration& duration) const noexcept
{
auto pred = [this, value] { return (m_value == value); };
std::unique_lock<std::mutex> lock(m_value_mutex);
if (pred()) return true;
return m_value_changed.wait_for(lock, duration, pred);
}
void wait_for(const T& value) const
{
try_wait_for(value, std::chrono::steady_clock::duration::max());
}
private:
// mutables so I could make the const promises on wait
// that they wont change the observable state (m_value)
// of this class.
mutable std::mutex m_value_mutex;
mutable std::condition_variable m_value_changed;
std::atomic<T> m_value;
};
//-----------------------------------------------------------------------------------------------------
// helper class for storing elapsed time, helps with readability later on
class ElapsedTime
{
public:
explicit ElapsedTime(const std::chrono::steady_clock::duration& duration) :
m_duration{ duration }
{
}
auto hours() const
{
return std::chrono::duration_cast<std::chrono::hours>(m_duration).count();
}
auto minutes() const
{
return (std::chrono::duration_cast<std::chrono::minutes>(m_duration).count() % 60);
}
auto seconds() const
{
return (std::chrono::duration_cast<std::chrono::seconds>(m_duration).count() % 60);
}
private:
std::chrono::steady_clock::duration m_duration;
};
//-----------------------------------------------------------------------------------------------------
// formatter for ElapsedTime
std::ostream& operator<<(std::ostream& os, const ElapsedTime& t)
{
os << std::setfill('0') << std::setw(2) << t.hours() << ':';
os << std::setfill('0') << std::setw(2) << t.minutes() << ':';
os << std::setfill('0') << std::setw(2) << t.seconds();
return os;
}
//-----------------------------------------------------------------------------------------------------
// ChronoMeter class
// note I use std::chrono classes
class ChronoMeter final
{
public:
ChronoMeter() :
m_state{ State::idle },
m_duration{ std::chrono::steady_clock::duration::min() }
{
};
ChronoMeter(const ChronoMeter&) = delete;
ChronoMeter(ChronoMeter&&) = delete;
ChronoMeter& operator=(const ChronoMeter&) = delete;
void Start()
{
m_start_time = std::chrono::steady_clock::now();
// exercise for the reader, also allow stopped Chronometers to be restarted.
// for now just this simple state model
if (m_state.set_if(State::idle, State::starting) != State::starting)
{
throw std::runtime_error("only an idle ChronoMeter can be started");
}
// it is okay to capture "this" because the destructor of the
// chronometer synchronizes with termination of this thread through the future
m_future = std::async(std::launch::async, [this]
{
// Set indication that the thread has really started.
// this is important because when std::async returns, this thread exists
// but may not have been scheduled yet.
m_state = State::running;
do
{
// assigning a value to m_duration isn't atomic so protect it.
// we might be reading the value on another thread which might
// result in reading an intermediate state.
std::scoped_lock<std::mutex> lock{ m_data_mtx };
m_duration = std::chrono::steady_clock::now() - m_start_time;
// using a statevariable to check for stopping means it can respond
// during the one second delay and stop immediately.
// this is an advantage over using sleep
} while (!m_state.try_wait_for(State::stopping, std::chrono::seconds(1)));
m_state = State::stopped;
});
// Wait for the thread to have really started
// this way we have a clear post condition for start
m_state.wait_for(State::running);
}
void Stop()
{
// only allow a running Chronometer to be stopped.
// in all other states Stop does nothing
if (m_state.set_if(State::running, State::stopping) == State::stopping)
{
// synchronization with stopped state, as set by other thread
m_state.wait_for(State::stopped);
// future get is not really needed for synchronization.
// but if thread threw an exception it's rethrown here
m_future.get();
}
}
~ChronoMeter()
{
// Automatically stop thread if this hasn't already happened.
Stop();
}
const ElapsedTime Elapsed() const
{
std::scoped_lock<std::mutex> lock{ m_data_mtx };
return ElapsedTime{ m_duration };
}
private:
std::future<void> m_future;
StateVariable<State> m_state;
mutable std::mutex m_data_mtx;
std::chrono::steady_clock::time_point m_start_time;
std::chrono::steady_clock::duration m_duration;
};
int main()
{
ChronoMeter meter1;
ChronoMeter meter2;
meter1.Start();
std::this_thread::sleep_for(std::chrono::seconds(5));
auto elapsed_1_1 = meter1.Elapsed();
std::cout << "Meter 1 elapsed time " << elapsed_1_1 << std::endl;
meter2.Start();
std::this_thread::sleep_for(std::chrono::seconds(4));
auto elapsed_1_2 = meter1.Elapsed();
auto elapsed_2 = meter2.Elapsed();
std::cout << "Meter 1 elapsed time " << elapsed_1_2 << std::endl;
std::cout << "Meter 2 elapsed time " << elapsed_2 << std::endl;
meter1.Stop();
// not stopping meter2 (and it's thread) explicitly, this is done safely by destructor if needed
return 0;
}
I usually solve this problem by making the multithreaded object handle everything that has to do with multithreading, here's how I solved it in your case (I ended up rewriting a lot of things so maybe the behavior isn't exactly the one you want, you can use my code as a starting point):
main.cpp:
#include <iostream>
#include <vector>
#include "mychrono.hpp"
int main()
{
std::vector<Chronometer*> car_crono;
for(int i = 0; i < 2; i++)
{
car_crono.push_back(new Chronometer);
}
while (1) {
// std::cout << "hello-world" << std::endl;
Chronometer::Time t = car_crono[0]->get_time();
if(t.sec >= 10)
car_crono[0]->reset_chrono();
std::cout << "Seconds of T0: " << t.sec << std::endl;
std::cout << "T1: " << car_crono[1]->to_string() << std::endl;
}
car_crono.clear();
}
mychrono.hpp:
#ifndef mychrono_hpp
#define mychrono_hpp
#include <iostream>
#include <thread>
#include <memory>
#include <condition_variable>
#include <mutex>
#include <atomic>
class Chronometer
{
public:
struct Time {
int hour;
int min;
int sec;
};
Chronometer();
void reset_chrono();
friend std::ostream& operator<<(std::ostream& flux, Chronometer& t);
Chronometer& operator=(const Chronometer& other);
std::string to_string();
Time get_time();
~Chronometer();
private:
Time currentTime;
std::mutex timeMutex;
std::condition_variable conditionVariable;
std::unique_ptr<std::thread> thread;
std::mutex CVMutex;
std::atomic<bool> exitNow;
void thread_function();
};
#endif
mychrono.cpp:
#include "mychrono.hpp"
Chronometer::Chronometer() : currentTime.hour(0), currentTime.min(0), currentTime.sec(0)
{
thread.reset(new std::thread(&Chronometer::thread_function, this));
}
void Chronometer::reset_chrono()
{
std::lock_guard<std::mutex> lock(timeMutex);
currentTime.hour = 0;
currentTime.sec = 0;
currentTime.min = 0;
}
std::ostream& operator<<(std::ostream& flux, Chronometer& t)
{
flux << t.to_string();
return flux;
}
Chronometer& Chronometer::operator=(const Chronometer& other)
{
// Guard self assignment
//if (this == &other)
return *this;
}
std::string Chronometer::to_string()
{
std::lock_guard<std::mutex> lock(timeMutex);
return std::to_string(currentTime.hour) + ":" + std::to_string(currentTime.min) + ":" + std::to_string(currentTime.sec);
}
Time Chronometer::get_time()
{
return currentTime;
}
Chronometer::~Chronometer()
{
exitNow = true;
{
std::unique_lock<std::mutex> lock(CVMutex);
lock.unlock();
conditionVariable.notify_all();
}
thread->join();
}
void Chronometer::thread_function()
{
std::unique_lock<std::mutex> waitLock(CVMutex);
while(!exitNow)
{
sec++;
if(currentTime.sec > 59) {
std::lock_guard<std::mutex> lock(timeMutex);
currentTime.min++;
currentTime.sec = 0;
}
if(currentTime.min > 59) {
std::lock_guard<std::mutex> lock(timeMutex);
currentTime.hour++;
currentTime.sec = 0;
currentTime.min = 0;
}
// std::cout << "chrono: " << *this << std::endl; //Not thread safe be careful
conditionVariable.wait_for(waitLock, std::chrono::seconds(1));
}
}
EDIT: About your latest comment: you don't need to reset a chrono in its destructor as the data will be destroyed anyway. If you want to reset the counter while it's running you want to call Chronometer::reset_chrono() from you main function.
For the second part of your comment, I added a get_time function to the code (I also added a mutex to avoid data races, I completly forgot when I wrote the original answer). When you want to get the current time of a chrono from the main function you just call get_time() and use the struct it returns to get the info you want.
I added a small example to show how to use both functions. As you can see, the main function doesn't even need to know what threads are !
I may be wrong but from the questions you ask I feel maybe you're not used to how multithreading works. It's a very difficult concept and one of the few I feel you can't learn only through experience, if that's the case you might want to learn about it from dedicated sites such as this one. I think I pieced together that you speak french, here's a really good article (that was never finished apparently) about the theory of it and another one in french, more about the specifics of C++. If you understand the core concepts and just have a hard time with my code, I plan on commenting it all but for now Pepijn Kramer did a great job explaining what they did in their response.
I am trying to create a data structure, ExpiringDeque. It should be somewhat similar to std::deque. Let's say I need only push_back(), size() and pop_front(). The data structure needs to automatically expire up to N first elements every T seconds.
This data structure needs to manage its own queue and expiration thread internally.
How do I write it in a thread safe way? This is an example that I came up with, does this seem reasonable? What am I missing?
#include <algorithm>
#include <atomic>
#include <cassert>
#include <deque>
#include <mutex>
#include <thread>
#include <unistd.h>
#include <iostream>
template <typename T>
class ExpiringDeque {
public:
ExpiringDeque(int n, int t) : numElements_(n), interval_(t), running_(true), items_({}) {
expiringThread_ = std::thread{[&] () {
using namespace std::chrono_literals;
int waitCounter = 0;
while (true) {
if (!running_) {
return;
}
std::this_thread::sleep_for(1s);
if (waitCounter++ < interval_) {
continue;
}
std::lock_guard<std::mutex> guard(mutex_);
waitCounter = 0;
int numToErase = std::min(numElements_, static_cast<int>(items_.size()));
std::cout << "Erasing " << numToErase << " elements\n";
items_.erase(items_.begin(), items_.begin() + numToErase);
}
}};
}
~ExpiringDeque() {
running_ = false;
expiringThread_.join();
}
T pop_front() {
if (items_.size() == 0) {
throw std::out_of_range("Empty deque");
}
std::lock_guard<std::mutex> guard(mutex_);
T item = items_.front();
items_.pop_front();
return item;
}
int size() {
std::lock_guard<std::mutex> guard(mutex_);
return items_.size();
}
void push_back(T item) {
std::lock_guard<std::mutex> guard(mutex_);
items_.push_back(item);
}
private:
int numElements_;
int interval_;
std::atomic<bool> running_;
std::thread expiringThread_;
std::mutex mutex_;
std::deque<T> items_;
};
int main() {
ExpiringDeque<int> ed(10, 3);
ed.push_back(1);
ed.push_back(2);
ed.push_back(3);
assert(ed.size() == 3);
assert(ed.pop_front() == 1);
assert(ed.size() == 2);
// wait for expiration
sleep(5);
assert(ed.size() == 0);
ed.push_back(10);
assert(ed.size() == 1);
assert(ed.pop_front() == 10);
return 0;
}
You can avoid an unnecessary wait in the destructor of ExpiringDeque by using a condition variable. I would also use std::condition_variable::wait_for with a predicate to check the running_ flag. This will ensure that you either wait for a timeout or a notification, whichever is earlier. You avoid using waitCounter and continue this way.
Another thing you should do is lock the mutex before checking the size of your deque in pop_front(), otherwise it's not thread safe.
Here's an updated version of your code:
template <typename T>
class ExpiringDeque {
public:
ExpiringDeque(int n, int t) : numElements_(n), interval_(t), running_(true), items_({}), cv_() {
expiringThread_ = std::thread{ [&]() {
using namespace std::chrono_literals;
while (true) {
//Wait for timeout or notification
std::unique_lock<std::mutex> lk(mutex_);
cv_.wait_for(lk, interval_ * 1s, [&] { return !running_; });
if (!running_)
return;
//Mutex is locked already - no need to lock again
int numToErase = std::min(numElements_, static_cast<int>(items_.size()));
std::cout << "Erasing " << numToErase << " elements\n";
items_.erase(items_.begin(), items_.begin() + numToErase);
}
} };
}
~ExpiringDeque() {
//Set flag and notify worker thread
{
std::lock_guard<std::mutex> lk(mutex_);
running_ = false;
}
cv_.notify_one();
expiringThread_.join();
}
T pop_front() {
std::lock_guard<std::mutex> guard(mutex_);
if (items_.size() == 0) {
throw std::out_of_range("Empty deque");
}
T item = items_.front();
items_.pop_front();
return item;
}
...
private:
int numElements_;
int interval_;
bool running_;
std::thread expiringThread_;
std::mutex mutex_;
std::deque<T> items_;
std::condition_variable cv_;
};
You can make the running_ flag a normal bool since the std::condition_variable::wait_for atomically checks for the timeout or notification.
Assume that there is a class which contains some data and calculates some results given queries, and the queries take a relatively large amount of time.
An example class (everything dummy) is:
#include <vector>
#include <numeric>
#include <thread>
struct do_some_work
{
do_some_work(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
{}
void update_query(size_t x) {
if (x < _data.size()) {
_current_query = x;
recalculate_result();
}
}
int get_result() const {
return _last_calculated_result;
}
private:
void recalculate_result() {
//dummy some work here
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
_last_calculated_result = std::accumulate(_data.cbegin(), _data.cbegin() + _current_query, 0);
}
std::vector<int> const _data;
size_t _current_query;
int _last_calculated_result;
};
and this can be used in the main code like:
#include <algorithm>
int main()
{
//make some dummy data
std::vector<int> test_data(20, 0);
std::iota(test_data.begin(), test_data.end(), 0);
{
do_some_work work(test_data);
for (size_t i = 0; i < test_data.size(); ++i) {
work.update_query(i);
std::cout << "result = {" << i << "," << work.get_result() << "}" << std::endl;
}
}
}
The above will wait in the main function a lot.
Now, assuming we want to run this querying in a tight loop (say GUI) and only care about about getting a "recent" result quickly when we query.
So, we want to move the work to a separate thread which calculates the results, and updates it, and when we get result, we get the last calculated one. That is, we want to change do_some_work class to do its work on a thread, with minimal changes (essentially find a pattern of changes that can be applied to (mostly) any class of this type).
My stab at this is the following:
#include <vector>
#include <numeric>
#include <mutex>
#include <thread>
#include <condition_variable>
#include <iostream>
struct do_lots_of_work
{
do_lots_of_work(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
, _worker()
, _data_mtx()
, _result_mtx()
, _cv()
, _do_exit(false)
, _work_available(false)
{
start_worker();
}
void update_query(size_t x) {
{
if (x < _data.size()) {
std::lock_guard<std::mutex> lck(_data_mtx);
_current_query = x;
_work_available = true;
_cv.notify_one();
}
}
}
int get_result() const {
std::lock_guard<std::mutex> lck(_result_mtx);
return _last_calculated_result;
}
~do_lots_of_work() {
stop_worker();
}
private:
void start_worker() {
if (!_worker.joinable()) {
std::cout << "starting worker..." << std::endl;
_worker = std::thread(&do_lots_of_work::worker_loop, this);
}
}
void stop_worker() {
std::cout << "worker stopping..." << std::endl;
if (_worker.joinable()) {
std::unique_lock<std::mutex> lck(_data_mtx);
_do_exit = true;
lck.unlock();
_cv.notify_one();
_worker.join();
}
std::cout << "worker stopped" << std::endl;
}
void worker_loop() {
std::cout << "worker started" << std::endl;
while (true) {
std::unique_lock<std::mutex> lck(_data_mtx);
_cv.wait(lck, [this]() {return _work_available || _do_exit; });
if (_do_exit) { break; }
if (_work_available) {
_work_available = false;
int query = _current_query; //take local copy
lck.unlock(); //unlock before doing lots of work.
recalculate_result(query);
}
}
}
void recalculate_result(int query) {
//dummy lots of work here
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
int const result = std::accumulate(_data.cbegin(), _data.cbegin() + query, 0);
set_result(result);
}
void set_result(int result) {
std::lock_guard<std::mutex> lck(_result_mtx);
_last_calculated_result = result;
}
std::vector<int> const _data;
size_t _current_query;
int _last_calculated_result;
std::thread _worker;
mutable std::mutex _data_mtx;
mutable std::mutex _result_mtx;
std::condition_variable _cv;
bool _do_exit;
bool _work_available;
};
and the usage is (example):
#include <algorithm>
int main()
{
//make some dummy data
std::vector<int> test_data(20, 0);
std::iota(test_data.begin(), test_data.end(), 0);
{
do_lots_of_work work(test_data);
for (size_t i = 0; i < test_data.size(); ++i) {
work.update_query(i);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cout << "result = {" << i << "," << work.get_result() << "}" << std::endl;
}
}
}
This seems to work, giving the last result, not stopping the main function etc.
But, this looks a LOT of changes are required to add a worker thread to a simple class like do_some_work. Items like two mutexes (one for the worker/main interaction data, and one for the result), one condition_variable, one more-work-available flag and one do-exit flag, that is quite a bit. I guess we don't want an async kind of mechanism because we don't want to potentially launch a new thread every time.
Now, I am not sure if there is a MUCH simpler pattern to make this kind of change, but it feels like there should be. A kind of pattern that can be used to off-load work to a thread.
So finally, my question is, can do_some_work be converted into do_lots_of_work in a much simpler way than the implementation above?
Edit (Solution 1) ThreadPool based:
Using a threadpool, the worker loop can be skipped, we need two mutexes, for result and query. Lock in updating query, Lock in getting result, Both lock in recalculate (take a local copy of a query, and write to result).
Note: Also, when pushing work on the queue, as we do not care about the older results, we can clear the work queue.
Example implementation (using the CTPL threadpool)
#include "CTPL\ctpl_stl.h"
#include <vector>
#include <mutex>
struct do_lots_of_work_with_threadpool
{
do_lots_of_work_with_threadpool(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
, _pool(1)
, _result_mtx()
, _query_mtx()
{
}
void update_query(size_t x) {
if (x < _data.size()) {
std::lock_guard<std::mutex> lck(_query_mtx);
_current_query = x;
}
_pool.clear_queue(); //clear as we don't want to calculate any out-date results.
_pool.push([this](int id) { recalculate_result(); });
}
int get_result() const {
std::lock_guard<std::mutex> lck(_result_mtx);
return _last_calculated_result;
}
private:
void recalculate_result() {
//dummy some work here
size_t query;
{
std::lock_guard<std::mutex> lck(_query_mtx);
query = _current_query;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
int result = std::accumulate(_data.cbegin(), _data.cbegin() + query, 0);
{
std::lock_guard<std::mutex> lck(_result_mtx);
_last_calculated_result = result;
}
}
std::vector<int> const _data;
size_t _current_query;
int _last_calculated_result;
ctpl::thread_pool _pool;
mutable std::mutex _result_mtx;
mutable std::mutex _query_mtx;
};
Edit (Solution 2) With ThreadPool and Atomic:
This solution changes the shared variables to atomic, and so we do not need any mutexes and do not have to consider taking/releasing locks etc. This is much simpler and very close to the original class (of course assumes a threadpool type exists somewhere as it is not part of the standard).
#include "CTPL\ctpl_stl.h"
#include <vector>
#include <mutex>
#include <atomic>
struct do_lots_of_work_with_threadpool_and_atomics
{
do_lots_of_work_with_threadpool_and_atomics(std::vector<int> data)
: _data(std::move(data))
, _current_query(0)
, _last_calculated_result(0)
, _pool(1)
{
}
void update_query(size_t x) {
if (x < _data.size()) {
_current_query.store(x);
}
_pool.clear_queue(); //clear as we don't want to calculate any out-date results.
_pool.push([this](int id) { recalculate_result(); });
}
int get_result() const {
return _last_calculated_result.load();
}
private:
void recalculate_result() {
//dummy some work here
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
_last_calculated_result.store(std::accumulate(_data.cbegin(), _data.cbegin() + _current_query.load(), 0));
}
std::vector<int> const _data;
std::atomic<size_t> _current_query;
std::atomic<int> _last_calculated_result;
ctpl::thread_pool _pool;
};
In my code I want my system to sleep, until a condition has been met. An after having searched i have found #include <unistd.h>, but to me it just looks like it sleeps until the time frame has been met. I was wondering if there was a easy way to make the program wait until the condition has been reached.
Here you have a sample of the code to clarify my point
bool check() {
while (condition) {
sleep.here();
} else {
run.the.rest();
}
}
Based on your incomplete pseudo-code and description, here is a class contidion_t, where you can set your condition via set_condition, and a thread blocking in loop will wake up, every time you set it.
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
struct condition_t {
public:
template <typename T>
void loop(T the_rest) {
while(running) {
std::unique_lock<std::mutex> lock_guard(m);
cv.wait(lock_guard, [this] { return ready.load(); });
the_rest();
ready = false;
}
}
void set_condition(bool value) {
ready = value;
if (value) {
cv.notify_one();
}
}
void stop_running() {
running = false;
ready = true;
cv.notify_all();
}
~condition_t () {stop_running();}
private:
std::mutex m;
std::condition_variable cv;
std::atomic<bool> ready{false};
std::atomic<bool> running{true};
};
int main() {
condition_t condition;
std::thread thread(&condition_t::loop<void (void)>, &condition, [] () {
std::cout << "Doing the rest" << std::endl;
});
std::cout << "Thread created but waits\nInput something for continue:";
int something;
std::cin >> something;
std::cout << "Continueing\n";
condition.set_condition(true);
std::cout << "Input something to stop running:";
std::cin >> something;
condition.stop_running();
thread.join();
}