4

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?

manlio
  • 18,345
  • 14
  • 76
  • 126
Bob
  • 10,741
  • 27
  • 89
  • 143
  • Use a condition variable and measure elapsed time outside the spawned thread. Signal as needed. – user1095108 Mar 10 '14 at 05:54
  • Don't use a thread. Use a timer instead. Then you can modify the timer's interval. You also gain other benefits such as the ability to *stop* the timer without having to wait for the sleep, and you don't have a sleeping thread occupying memory or clogging up your thread pool. – Jim Mischel Mar 10 '14 at 14:29

2 Answers2

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

sehe
  • 374,641
  • 47
  • 450
  • 633
0

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.

6EQUJ5
  • 3,142
  • 1
  • 21
  • 29