Resetting sleeping time of a thread

自作多情 提交于 2021-01-27 07:42:42

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!