Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stopping long-sleep threads

Let's suppose I have a thread which should perform some task periodically but this period is 6 times each hour 12 times each hour (every 5 minutes), I've often seen code which controls the thread loop with a is_running flag which is checked every loop, like this:

std::atomic<bool> is_running;  void start() {     is_running.store(true);     std::thread { thread_function }.detach(); }  void stop() {     is_running.store(false); }  void thread_function() {     using namespace std::literals;     while (is_running.load())     {         // do some task...         std::this_thread::sleep_for(5min);     } } 

But if the stop() function is called, let's say, 1 millisecond after start() the thread would be alive for 299999 additional milliseconds until it awakes, checks the flag, and die.

Is my understanding correct? How to avoid keeping alive (but sleeping) a thread which should have been ended? My best approach until now is the following:

void thread_function() {     using namespace std::literals;     while (is_running.load())     {         // do some task...         for (unsigned int b = 0u, e = 1500u; is_running.load() && (b != e); ++b)         {             // 1500 * 200 = 300000ms = 5min             std::this_thread::sleep_for(200ms);         }     } } 

Is there a less-dirty and more straightforward way to achieve this?

like image 449
PaperBirdMaster Avatar asked Apr 21 '15 14:04

PaperBirdMaster


People also ask

How do I stop sleeping threads?

interrupt() method: If any thread is in sleeping or waiting for a state then using the interrupt() method, we can interrupt the execution of that thread by showing InterruptedException. A thread that is in the sleeping or waiting state can be interrupted with the help of the interrupt() method of Thread class.

Why thread sleep is not recommended?

Thread. sleep is bad! It blocks the current thread and renders it unusable for further work.

Does sleep Stop thread?

sleep() functions to execute, it always pauses the current thread execution. If any other thread interrupts when the thread is sleeping, then InterruptedException will be thrown.

How do I make my thread sleep for 1 minute?

To make a thread sleep for 1 minute, you do something like this: TimeUnit. MINUTES. sleep(1);


2 Answers

Use a condition variable. You wait on the condition variable or 5 minutes passing. Remember to check for spurious wakeups.

cppreference

I cannot find a good stack overflow post on how to use a condition variable in a minute or two of google searching. The tricky part is realizing that the wait can wake up with neither 5 minutes passing, nor a signal being sent. The cleanest way to handle this is to use the wait methods with a lambda that double-checks that the wakeup was a "good" one.

here is some sample code over at cppreference that uses wait_until with a lambda. (wait_for with a lambda is equivalent to wait_until with a lambda). I modified it slightly.

Here is an version:

struct timer_killer {   // returns false if killed:   template<class R, class P>   bool wait_for( std::chrono::duration<R,P> const& time ) const {     std::unique_lock<std::mutex> lock(m);     return !cv.wait_for(lock, time, [&]{return terminate;});   }   void kill() {     std::unique_lock<std::mutex> lock(m);     terminate=true; // should be modified inside mutex lock     cv.notify_all(); // it is safe, and *sometimes* optimal, to do this outside the lock   }   // I like to explicitly delete/default special member functions:   timer_killer() = default;   timer_killer(timer_killer&&)=delete;   timer_killer(timer_killer const&)=delete;   timer_killer& operator=(timer_killer&&)=delete;   timer_killer& operator=(timer_killer const&)=delete; private:   mutable std::condition_variable cv;   mutable std::mutex m;   bool terminate = false; }; 

live example.

You create a timer_killer in a shared spot. Client threads can wait_for( time ). If it returns false, it means you where killed before your wait was complete.

The controlling thread just calls kill() and everyone doing a wait_for gets a false return.

Note that there is some contention (locking of the mutex), so this isn't suitable for infinite threads (but few things are). Consider using a scheduler if you need to have an unbounded number of tasks that are run with arbitrary delays instead of a full thread per delayed repeating task -- each real thread is upwards of a megabyte of system address space used (just for the stack).

like image 145
Yakk - Adam Nevraumont Avatar answered Sep 29 '22 18:09

Yakk - Adam Nevraumont


There are two traditional ways you could do this.

You could use a timed wait on a condition variable, and have the other thread signal your periodic thread to wake up and die when it's time.

Alternately you could poll on a pipe with your sleep as a timeout instead of of sleeping. Then you just write a byte to the pipe and the thread wakes up and can exit.

like image 42
Mark B Avatar answered Sep 29 '22 19:09

Mark B