问题
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?
回答1:
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
回答2:
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.
来源:https://stackoverflow.com/questions/22292820/resetting-sleeping-time-of-a-thread