14

I am using an std::thread in my C++ code to constantly poll for some data & add it to a buffer. I use a C++ lambda to start the thread like this:

StartMyThread() {

    thread_running = true;
    the_thread = std::thread { [this] {
        while(thread_running) {
          GetData();
        }
    }};
}

thread_running is an atomic<bool> declared in class header. Here is my GetData function:

GetData() {
    //Some heavy logic which needs to be executed in a worker thread
}

Next I also have a StopMyThread function where I set thread_running to false so that it exits out of the while loop in the lambda block.

StopMyThread() {
  thread_running = false;
  the_thread.join();
}

It works well. The thread starts & stops without crashing.

This C++ code is used on iOS, Android, OS X and Windows. My application UI has a button which requires me to start & stop the thread on a button press; this button can be frequently used in some occasions. I can see a split second delay in UI while stopping or starting the thread.

My question is: In C++, is this a correct way to start/stop a thread frequently ? I think that with this logic I am creating a new thread every-time. And as I understand, creating a new thread makes the OS allocate lot of new resources which can be time-consoming. And I think this is the mistake I am doing. How can I avoid this ?

How can make use of the same thread without allocating new one repeatedly throughout the application lifecycle, and just play/pause it when required ?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
TheWaterProgrammer
  • 7,055
  • 12
  • 70
  • 159
  • The delay may also be you waiting for the old thread to exit. (If you have two threads using the same `thread_running` value, you will have grief!) – Martin Bonner supports Monica Nov 11 '16 at 09:08
  • 1
    You can pause a thread by making it wait on a `std::mutex`. – nwp Nov 11 '16 at 09:08
  • 1
    Beware: See https://blogs.msdn.microsoft.com/oldnewthing/20031209-00/?p=41573 for why a general `suspend_thread` function is not going to be a good idea. (The example is about C# on Windows, but the principles apply everywhere.) If you do use a mutex to make it wait, you will have to think carefully about blocking the UI thread. – Martin Bonner supports Monica Nov 11 '16 at 09:12
  • Not exactly on point, but related: take a look at boost::coroutine, it's a pretty cool thing. – SingerOfTheFall Nov 11 '16 at 09:13
  • 2
    `std::mutex` + `std::condition_variable`. The `resume` function will just do ` `notify_one` – Arunmu Nov 11 '16 at 09:14
  • @SingerOfTheFall Isnt there a `std:: C++` way of creating a `coroutine` ? – TheWaterProgrammer Nov 11 '16 at 10:32
  • @NelsonP, boost coroutines are pretty powerful. They can be paused/resumed from everywhere, and you can pass objects into a coroutine, and out from at any point. AFAIK they work by saving the context of the coroutine and then performing context switches when it needs to be stopped or resumed. I don't think you can quickly write something like that yourself only with std. – SingerOfTheFall Nov 11 '16 at 10:41
  • @SingerOfTheFall implementing coroutines and context switches is not that [difficult](https://github.com/user1095108/generic/blob/master/coroutine.hpp). – user1095108 Nov 11 '16 at 10:51
  • 1
    I know this question is old, but I just want to point out that "button pressed frequently" is still glacially slow compared to the speed of a computer. Stop and start the thread. – Sebastian Redl Jan 11 '22 at 09:05

1 Answers1

7

This is the classical example for the use of a condition variable. You wait on a mutex and notify a thread when a certain condition is fulfilled; this way you don't need to allocate a new thread when you need one, but this is not always a good thing, if you wish to save memory. An alternative would be a coroutine yielding to another coroutine when data is needed, which is arguably prettier. You need to implement coroutines yourself, or use a ready-made library, such as boost.coroutine.

Example

::std::condition_variable cv_;
::std::mutex m_;
bool data_is_ready_{};

StartMyThread()
{
  ::std::thread([this]
    {
      for (;;)
      {
        ::std::unique_lock<decltype(m_)> l(m_);
        cv_.wait(l, [this]{ return data_is_ready_; });

        // do your stuff, m_ is locked
        data_is_ready_ = false;
      }
    }
  ).detach();
}

To notify:

{
  ::std::unique_lock<decltype(m_)> l(m_);

  data_is_ready_ = true;
}

cv_.notify_one();

As it is often faster to free the lock before notifying, than vice-versa.

user1095108
  • 14,119
  • 9
  • 58
  • 116
  • 1
    Haven't you missed to lock the mutex inside the thread? cv.wait will not lock it, it expects a **locked** mutex and will **unlock** it during wait. – Matthias247 Nov 11 '16 at 09:40
  • 11
    A nit-pick. Why to fully qualify `std` namespace ? It's ugly and useless unless you did something nasty and wrong. – Arunmu Nov 11 '16 at 09:43
  • 1
    why do you use a `.detach` at the end. Wouldn't this make it a `daemon` thread ? – TheWaterProgrammer Nov 11 '16 at 09:44
  • Yeah, it's a detached thread, but he knows about join(), so he can do it differently. – user1095108 Nov 11 '16 at 10:45
  • One Last question all of you participants : `thread_running` is an `atomic` as I have said in my question. I can just keep returning out of `GetData()` if `thread_running` is false. And only execute the complete method logic if `thread_running` is true. Wouldn't that be more efficient here ? – TheWaterProgrammer Nov 11 '16 at 16:39
  • @NelsonP With the above approach you don't need atomics anymore as reads and writes are protected by a mutex. That said I envisioned data_ready_ as an alias for thread_running as the thread should not run if data is not ready. – user1095108 Nov 11 '16 at 17:31
  • @user1095108 Accepted. But I want to compare another solution here. Is there a disadvantage is I do the same with an `atomic variable thread_running` ? – TheWaterProgrammer Nov 11 '16 at 17:42
  • I have asked a follow up question to this question [here](http://stackoverflow.com/questions/40553609/approach-of-using-an-stdatomic-compared-to-stdcondition-variable-wrt-pausing). Please reply if anyone can. thanks – TheWaterProgrammer Nov 11 '16 at 17:56