122

How can I check if a std::thread is still running (in a platform independent way)? It lacks a timed_join() method and joinable() is not meant for that.

I thought of locking a mutex with a std::lock_guard in the thread and using the try_lock() method of the mutex to determine if it is still locked (the thread is running), but it seems unnecessarily complex to me.

Do you know a more elegant method?

Update: To be clear: I want to check if the thread cleanly exited or not. A 'hanging' thread is considered running for this purpose.

Patrizio Bertoni
  • 2,582
  • 31
  • 43
kispaljr
  • 1,942
  • 2
  • 16
  • 22
  • I guess checking if a thread is still running only matters when you expect to `wait()` for it and, if so, if you haven't `wait()` 'ed for it yet, he must be running by definition. But this reasoning might be inexact. – ereOn Feb 01 '12 at 10:48
  • Actually I have a thread that exits on exceptional conditions, and I want to check from the main thread if it is still running, but do not want to wait for (join) it – kispaljr Feb 01 '12 at 10:55
  • 1
    What exactly do you mean by running? Do you mean it is actively processing rather than in a wait state, or do you mean the thread still exists and has not terminated? – CashCow Feb 01 '12 at 10:58
  • You could always use boost :) – CashCow Feb 01 '12 at 11:52
  • 4
    You shouldn't have accepted an answer if you weren't satisfied with it. – Nicol Bolas Feb 01 '12 at 17:16
  • @kispaljr: How do you define "exited cleanly"? Do you mean whether it threw an exception? Because that's the only way a thread in C++ can exit without returning from its main function. – Nicol Bolas Feb 01 '12 at 17:17
  • Sorry about the false accept, I've learnt from it. By "exited cleanly" I meant by calling 'return' – kispaljr Feb 06 '12 at 15:48
  • If you only want to check its status you can use any atomic boolean or integer. If you want to be able to do that and also have the thread run detached but be able to still wait for it if necessary, your method of using a mutex (or a semaphore) is something I have now found is a good policy. These use less resources than a zombie thread. – CashCow Jul 27 '16 at 08:46
  • @CashCow That really doesn't make any sense. If it can release a mutex, it can also change the value of a `std::atomic` type. So why use a mutex? It's heavier, harder to test, and risks false results if checked from two threads at the same time. – David Schwartz Mar 07 '17 at 02:50
  • mutexes are better when you want to wait for a period without using CPU. Spinlocks are better only for rare race conditions. – CashCow Mar 09 '17 at 10:05

9 Answers9

161

If you are willing to make use of C++11 std::async and std::future for running your tasks, then you can utilize the wait_for function of std::future to check if the thread is still running in a neat way like this:

#include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    /* Run some task on new thread. The launch policy std::launch::async
       makes sure that the task is run asynchronously on a new thread. */
    auto future = std::async(std::launch::async, [] {
        std::this_thread::sleep_for(3s);
        return 8;
    });

    // Use wait_for() with zero milliseconds to check thread status.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    auto result = future.get(); // Get result.
}

If you must use std::thread then you can use std::promise to get a future object:

#include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    // Create a promise and get its future.
    std::promise<bool> p;
    auto future = p.get_future();

    // Run some task on a new thread.
    std::thread t([&p] {
        std::this_thread::sleep_for(3s);
        p.set_value(true); // Is done atomically.
    });

    // Get thread status using wait_for as before.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    t.join(); // Join thread.
}

Both of these examples will output:

Thread still running

This is of course because the thread status is checked before the task is finished.

But then again, it might be simpler to just do it like others have already mentioned:

#include <thread>
#include <atomic>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    std::atomic<bool> done(false); // Use an atomic flag.

    /* Run some task on a new thread.
       Make sure to set the done flag to true when finished. */
    std::thread t([&done] {
        std::this_thread::sleep_for(3s);
        done = true;
    });

    // Print status.
    if (done) {
        std::cout << "Thread finished" << std::endl;
    } else {
        std::cout << "Thread still running" << std::endl;
    }

    t.join(); // Join thread.
}

Edit:

There's also the std::packaged_task for use with std::thread for a cleaner solution than using std::promise:

#include <future>
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;

    // Create a packaged_task using some task and get its future.
    std::packaged_task<void()> task([] {
        std::this_thread::sleep_for(3s);
    });
    auto future = task.get_future();

    // Run task on new thread.
    std::thread t(std::move(task));

    // Get thread status using wait_for as before.
    auto status = future.wait_for(0ms);

    // Print status.
    if (status == std::future_status::ready) {
        // ...
    }

    t.join(); // Join thread.
}
Felix Glas
  • 15,065
  • 7
  • 53
  • 82
  • 2
    Nice answer. I would add that it also works with threads without any return value and future – kispaljr Jun 20 '13 at 08:11
  • What is the reason for this code `std::atomic done(false);`? Isn't the `bool` atomic by default? – Hi-Angel Aug 07 '14 at 15:16
  • 8
    @YagamyLight In C++ nothing is atomic by default unless it is wrapped in an `std::atomic`. `sizeof(bool)` is implementation defined and may be > 1, so it's possible that a partial write can occur. There's also the issue of cache coherence.. – Felix Glas Aug 07 '14 at 16:17
  • 2
    @YagamyLight More info here: [When do I really need to use atomic instead of bool?](http://stackoverflow.com/q/16320838/873025) – Felix Glas Aug 07 '14 at 16:23
  • Is wait_for( 0 ) guaranteed in what it returns? – CashCow Jul 27 '16 at 08:48
  • 2
    note that std::chrono_literals will require C++14 to compile – Patrizio Bertoni Oct 30 '17 at 09:38
  • Note that the `std::atomic` requires the thread to write it's status to it. This might go awry if there is an early return or even an exception! – mxmlnkn Jan 26 '20 at 18:23
  • This is horribly impractical and unscalable. You're creating a ton of global variables for each thread... Why is such a simple feature so difficult to implement in C++? – Cerin Dec 24 '20 at 04:22
  • The approach with `std::packaged_task` can cause the future to always return `std::future_status::deferred`. See also https://stackoverflow.com/a/45289981/2279059. Unless there is a specific reason to use `std::thread`, it is better to use `std::async` with `std::launch::async`. See also https://www.slideshare.net/KuoE0/effective-modern-c-item-35-36 – Florian Winter Sep 10 '21 at 10:52
8

An easy solution is to have a boolean variable that the thread sets to true on regular intervals, and that is checked and set to false by the thread wanting to know the status. If the variable is false for to long then the thread is no longer considered active.

A more thread-safe way is to have a counter that is increased by the child thread, and the main thread compares the counter to a stored value and if the same after too long time then the child thread is considered not active.

Note however, there is no way in C++11 to actually kill or remove a thread that has hanged.

Edit How to check if a thread has cleanly exited or not: Basically the same technique as described in the first paragraph; Have a boolean variable initialized to false. The last thing the child thread does is set it to true. The main thread can then check that variable, and if true do a join on the child thread without much (if any) blocking.

Edit2 If the thread exits due to an exception, then have two thread "main" functions: The first one have a try-catch inside which it calls the second "real" main thread function. This first main function sets the "have_exited" variable. Something like this:

std::atomic<bool> thread_done = false;

void *thread_function(void *arg)
{
    void *res = nullptr;

    try
    {
        res = real_thread_function(arg);
    }
    catch (...)
    {
    }

    thread_done = true;

    return res;
}
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 1
    If that is the OP's definition of "running". – CashCow Feb 01 '12 at 10:57
  • Maybe you misunderstood me. I want to check if a thread has cleanly exited or not. Sorry for the unclear wording. – kispaljr Feb 01 '12 at 10:59
  • @kispaljr It's okay. I have updated my answer after your clarification. – Some programmer dude Feb 01 '12 at 11:16
  • thanks, naturally that can work, so I accepted your answer, but I guess that means that there is no elegant solution for this. Thanks, anyway – kispaljr Feb 01 '12 at 12:34
  • 8
    If different threads are reading and writing `thread_done`, then this code is broken without a memory barrier. Use `std::atomic` instead. – ildjarn Feb 01 '12 at 19:36
  • @ildjarn If using multiple threads, then have one "done" variable per thread. Maybe in a map, like `std::map`. – Some programmer dude Feb 02 '12 at 06:21
  • 1
    I wasn't referring to multiple worker threads, I was referring to a single worker thread writing to the `bool` while the main thread reads from it -- this **needs** a memory barrier. – ildjarn Feb 02 '12 at 16:43
  • 5
    Check out [this question](http://stackoverflow.com/questions/14365595/is-it-necessary-to-use-a-stdatomic-to-signal-that-a-thread-has-finished-execut) for a discussion why a `std::atomic` is necessary here. – Robert Rüger Jan 26 '13 at 15:41
6

This simple mechanism you can use for detecting finishing of a thread without blocking in join method.

std::thread thread([&thread]() {
    sleep(3);
    thread.detach();
});

while(thread.joinable())
    sleep(1);
Evgeny Karpov
  • 2,386
  • 26
  • 16
  • 2
    detaching the thread is eventually not what one wants, and if you don't, you have to call `join()` from some thread that does not wait for the thread to loose its `joinable()` property, otherwise it will loop endlessly (i.e. `joinable()` returns true until the thread is actually `join()`ed and not until its finished) – Niklas R Jul 18 '16 at 13:31
  • joinable means a thread is holding a thread handle. if thread is done it will be still joinable. if you need to check without waiting the end of the thread, here is the solution. It is just a few lines of code. Why you didn't try it first? – Evgeny Karpov Jul 18 '16 at 16:06
  • I did, and the point I am trying to make is, that if you remove the `thread.detach()` part, the program above will never terminate. – Niklas R Jul 18 '16 at 16:13
  • yes, it will not. it's why it calls detaching in the end. – Evgeny Karpov Jul 18 '16 at 16:20
  • 2
    This method of calling detach dispenses with the need for mutex and other more complex solutions. I use it and it works! Thanks for the answer. – sep Dec 29 '16 at 07:23
  • Hi, new to multiprocessing here. Can you explain that syntax of calling a thread? I couldn't find that in docs. – infiNity9819 Apr 25 '21 at 16:06
  • What compiler are you using that lets you capture `thread` before it's fully declared? Not working on VS2017. – genpfault Nov 11 '21 at 17:00
5

You can always check if the thread's id is different than std::thread::id() default constructed. A Running thread has always a genuine associated id. Try to avoid too much fancy stuff :)

Michal Turlik
  • 192
  • 2
  • 6
1

Create a mutex that the running thread and the calling thread both have access to. When the running thread starts it locks the mutex, and when it ends it unlocks the mutex. To check if the thread is still running, the calling thread calls mutex.try_lock(). The return value of that is the status of the thread. (Just make sure to unlock the mutex if the try_lock worked)

One small problem with this, mutex.try_lock() will return false between the time the thread is created, and when it locks the mutex, but this can be avoided using a slightly more complex method.

Nathan Fox
  • 582
  • 6
  • 16
  • 1
    -1 You should not use `std::mutex` for this kind of signalling (mostly due to reasons how mutex is usually implemented). An `atomic_flag` works just as well in this case with way less overhead. A `std::future` might be even better as it expresses the intent more clearly. Also, remember that `try_lock` may fail spuriously, so the return is _not_ necessarily the status of the thread (although it probably won't hurt you much in this particular case). – ComicSansMS Jun 19 '13 at 12:18
0

I checked both systems: -Using thread+atomic: take 9738 milliseconds -Using future+async: take 7746 milliseconds Not threads: 56000milliseconds Using a Core-I7 6 cores laptop

My code creates 4000 threads, but no more than 12 running every time.

Here is the code:

#include <iostream>
#include <thread>
#include <future>
#include <chrono>
#include <mutex>          // std::mutex
#include <atomic>
#include <chrono>


#pragma warning(disable:4996)
#pragma warning(disable:6031)
#pragma warning(disable:6387)//strout
#pragma warning(disable:26451)

using namespace std;
const bool FLAG_IMPRIME = false;
const int MAX_THREADS = 12;


mutex mtx;           // mutex for critical section
atomic <bool> th_end[MAX_THREADS];
atomic <int> tareas_acabadas;

typedef std::chrono::high_resolution_clock t_clock; //SOLO EN WINDOWS
std::chrono::time_point<t_clock> start_time, stop_time; char null_char;
void timer(const char* title = 0, int data_size = 1) { stop_time = t_clock::now(); double us = (double)chrono::duration_cast<chrono::microseconds>(stop_time - start_time).count(); if (title) printf("%s time = %7lgms = %7lg MOPs\n", title, (double)us * 1e-3, (double)data_size / us); start_time = t_clock::now(); }



class c_trim
{
    char line[200];
    thread th[MAX_THREADS];
    double th_result[MAX_THREADS];
    int th_index;
    double milliseconds_commanded;
    void hilo(int hindex,int milliseconds, double& milliseconds2)
    {
        sprintf(line, "%i:%ia ",hindex, milliseconds); imprime(line);
        this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
        milliseconds2 = milliseconds * 1000;
        sprintf(line, "%i:%ib ", hindex, milliseconds); imprime(line);
        tareas_acabadas++;  th_end[hindex] = true;
    }
    int wait_first();
    void imprime(char* str) { if (FLAG_IMPRIME) { mtx.lock(); cout << str; mtx.unlock(); } }

public:
    void lanzatareas();


    vector <future<void>> futures;
    int wait_first_future();
    void lanzatareas_future();//usa future
};

int main()
{
    c_trim trim;
    timer();
    trim.lanzatareas();
    cout << endl;
    timer("4000 tareas using THREAD+ATOMIC:", 4000);
    trim.lanzatareas_future();
    cout << endl;
    timer("4000 tareas using FUTURE:", 4000);
    cout << endl << "Tareas acabadas:" << tareas_acabadas << endl;
    cout << "=== END ===\n"; (void)getchar();
}

void c_trim::lanzatareas()
{
    th_index = 0;
    tareas_acabadas = 0;
    milliseconds_commanded = 0;
    double *timeout=new double[MAX_THREADS];
    int i;
    for (i = 0; i < MAX_THREADS; i++)
    {
        th_end[i] = true;
        th_result[i] = timeout[i] = -1;
    }


    for (i = 0; i < 4000; i++)
    {
        int milliseconds = 5 + (i % 10) * 2;
        {
            int j = wait_first();
            if (th[j].joinable())
            {
                th[j].join();
                th_result[j] = timeout[j];
            }
            milliseconds_commanded += milliseconds;
            th_end[j] = false;
            th[j] = thread(&c_trim::hilo, this, j, milliseconds, std::ref(timeout[j]));
        }
    }
    for (int j = 0; j < MAX_THREADS; j++)
        if (th[j].joinable())
        {
            th[j].join();
            th_result[j] = timeout[j];
        }

    delete[] timeout;
    cout <<endl<< "Milliseconds commanded to wait=" << milliseconds_commanded << endl;
}

void c_trim::lanzatareas_future()
{
    futures.clear();
    futures.resize(MAX_THREADS);

    tareas_acabadas = 0;
    milliseconds_commanded = 0;
    double* timeout = new double[MAX_THREADS];
    int i;
    for (i = 0; i < MAX_THREADS; i++)
    {
        th_result[i] = timeout[i] = -1;
    }


    for (i = 0; i < 4000; i++)
    {
        int milliseconds = 5 + (i % 10) * 2;
        {
            int j;
            if (i < MAX_THREADS) j = i;
            else
            {
                j = wait_first_future();
                futures[j].get();
                th_result[j] = timeout[j];
            }
            milliseconds_commanded += milliseconds;
            futures[j] = std::async(std::launch::async, &c_trim::hilo, this, j, milliseconds, std::ref(timeout[j]));
        }
    }
    //Last MAX_THREADS:
    for (int j = 0; j < MAX_THREADS; j++)
    {
        futures[j].get();
        th_result[j] = timeout[j];
    }

    delete[] timeout;
    cout << endl << "Milliseconds commanded to wait=" << milliseconds_commanded << endl;
}

int c_trim::wait_first()
{
    int i;
    while (1)
        for (i = 0; i < MAX_THREADS; i++)
        {
            if (th_end[i] == true)
            {
                return i;
            }
        }
}



//Espera que acabe algun future y da su index
int c_trim::wait_first_future()
{
    int i;
    std::future_status status;
    while (1)
        for (i = 0; i < MAX_THREADS; i++)
        {
            status = futures[i].wait_for(0ms);
            if (status == std::future_status::ready)
                return i;
        }
}
mathengineer
  • 140
  • 6
0

I also had this problem very recently. Tried with the C++20 std::jthread using the shared-stop state to check if the thread is over, but inside the thread the std::stop_token argument is a readonly and doesn't indicate to outside when the thread finishes.

So I created a simple class (nes::uthread) extending std::thread with a flag to indicate it's finished. Example:

#include <atomic>
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>

namespace nes {

  class uthread final
  {
    std::unique_ptr<std::atomic<bool>> m_finished;
    std::thread m_thr;

  public:
    uthread()
      : m_finished { std::make_unique<std::atomic<bool>>(true) }
    {}

    template <class Function, class... Args>
    uthread(Function&& f, Args&&... args)
      : m_finished { std::make_unique<std::atomic<bool>>(false) }
      , m_thr {
        [](std::atomic<bool>& finished, Function&& ff, Args&&... aargs) {
          try {
            std::forward<Function>(ff)(std::forward<Args>(aargs)...);
            finished = true;
          } catch (...) {
            finished = true;
            throw;
          }
        }, 
        std::ref(*m_finished), std::forward<Function>(f), 
        std::forward<Args>(args)...
      }
    {}

    uthread(const uthread&) = delete;
    uthread(uthread&&) = default;
    uthread& operator=(const uthread&) = delete;
    uthread& operator=(uthread&&) = default;

    [[nodiscard]] std::thread::id get_id() const noexcept { 
      return m_thr.get_id(); }
    [[nodiscard]] bool joinable() const noexcept { return m_thr.joinable(); }
    void join() { m_thr.join(); }
    [[nodiscard]] const std::atomic<bool>& finished() const noexcept { 
      return *m_finished; }
  };

}

int main()
{
    using namespace std;
    using namespace std::chrono;
    using namespace std::chrono_literals;
    using namespace nes;

    {
      cout << "std::thread join() termination\n";

      atomic<bool> finished = false;
      thread t { [&finished] {
        this_thread::sleep_for(2s);
        finished = true;
        cout << "thread ended\n";
      }};

      for (int i = 0; i < 5; i++) {
        cout << t.get_id() << ".join() " << t.joinable() 
             << " finished: " << finished << '\n';
        this_thread::sleep_for(1s);
      }

      t.join();
    }
    cout << '\n';

    {
      cout << "std::jthread join() termination\n";

      jthread t {[](stop_token st) {
        this_thread::sleep_for(2s);
        cout << "thread ended. stop possible: " << st.stop_possible() << '\n';
      }};

      auto st = t.get_stop_source();
      for (int i = 0; i < 5; i++) {
        cout << t.get_id() << ".join() " << t.joinable() 
             << " finished: " << !st.stop_possible() << '\n';
        this_thread::sleep_for(1s);
      }
    }
    cout << '\n';

    {
      cout << "nes::uthread join() termination\n";

      uthread t {[] {
        this_thread::sleep_for(2s);
        cout << "thread ended\n";
      }};

      for (int i = 0; i < 5; i++) {
        cout << t.get_id() << ".join() " << t.joinable() 
             << " finished: " << t.finished() << '\n';
        this_thread::sleep_for(1s);
      }

      t.join();
    }
}

Possible prints:

std::thread join() termination
2.join() 1 finished: 0
2.join() 1 finished: 0
thread ended
2.join() 1 finished: 1
2.join() 1 finished: 1
2.join() 1 finished: 1

std::jthread join() termination
3.join() 1 finished: 0
3.join() 1 finished: 0
thread ended. stop possible: 1
3.join() 1 finished: 0
3.join() 1 finished: 0
3.join() 1 finished: 0

nes::uthread join() termination
4.join() 1 finished: 0
4.join() 1 finished: 0
thread ended
4.join() 1 finished: 1
4.join() 1 finished: 1
4.join() 1 finished: 1

You can use std::jthread in nes::uthread so you don't need to join.

Nico Engels
  • 407
  • 2
  • 12
0

I just had a breakthrough with this. I've been looking for a simple and general solution. Here it is.

Using the Boost C++ Libraries, create a signal that returns a boolean. Prior to its first use, connect the signal to a slot that returns false. Create a boost::signals2::scoped_connection within your thread that returns true. This exploits the default behavior of boost::signals2::signal return types that returns the value of the last slot called. By using the scoped_connection at the beginning of your thread, this slot only remains connected while thread is running. Also, Boost signals2::signals contain internal mutexes that maintain thread safety.

#include <boost/signals2.hpp>

int main(int argc, char* argv[])
{
   //This can be in a class or someplace else
   boost::signals2::signal<bool ()> ThreadRunning;

   // Be sure to capture 'this' if used in a class
   ThreadRunning.connect([]() {return false;});
   
   auto t = std::thread([]()
   {
      boost::signals2::scoped_connection c(ThreadRunning.connect[]() {return true;}));
      // Do your stuff.
   });

   if (TreadRunning())
      //Do stuff if the thread is still running

   t.join();

   return 0;
}

While this method is threadsafe, there is the possibility that the thread will complete between when you call your signal and completing whatever you use this logic for.

0

Surely have a mutex-wrapped variable initialised to false, that the thread sets to true as the last thing it does before exiting. Is that atomic enough for your needs?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 2
    If you use a mutex anyway, then I feel my solution (using only the mutex, w/o the boolean) to be more elegant. If you absolutely want to use a thread-safe boolean I would recommend std::atomic instead. In most implementations it will be lock-free. – kispaljr Feb 01 '12 at 12:36
  • Why lock at all? One thread only ever reads, one only ever writes. And word-size writes are atomic in any case IIRC. – Xeo Feb 01 '12 at 14:11
  • 3
    @Xeo : The write may be atomic, but a memory barrier is still needed if you expect to see the written value on a different thread (which may be executing on a different CPU). `std::atomic` takes care of this for you, which is why _it's_ the real answer IMO. – ildjarn Feb 01 '12 at 19:37