简体   繁体   English

如何检查 std::thread 是否仍在运行?

[英]How to check if a std::thread is still running?

How can I check if a std::thread is still running (in a platform independent way)?如何检查std::thread是否仍在运行(以独立于平台的方式)? It lacks a timed_join() method and joinable() is not meant for that.它缺少一个timed_join()方法,而 joinable( joinable()不是为此而设计的。

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.我曾想过在线程中使用std::lock_guard锁定互斥锁,并使用互斥锁的try_lock()方法来确定它是否仍处于锁定状态(线程正在运行),但这对我来说似乎不必要地复杂。

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.为此目的,“挂起”线程被视为正在运行。

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:如果你愿意使用 C++11 std::asyncstd::future来运行你的任务,那么你可以利用std::futurewait_for函数来检查线程是否仍然以简洁的方式运行像这样:

#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:如果你必须使用std::thread那么你可以使用std::promise来获得一个未来的对象:

#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 :还有std::packaged_taskstd::thread一起使用以获得比使用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.
}

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.一个简单的解决方案是有一个布尔变量,线程定期将其设置为 true,并由想要知道状态的线程检查并设置为 false。 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.但是请注意,在 C++11 中无法真正杀死或删除已挂起的线程。

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.将布尔变量初始化为 false。 The last thing the child thread does is set it to true.子线程做的最后一件事是将其设置为 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. Edit2如果线程因异常而退出,则有两个线程“主”函数:第一个有一个try - catch ,它在其中调用第二个“真正的”主线程函数。 This first main function sets the "have_exited" variable.第一个主函数设置“have_exited”变量。 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;
}

You can always check if the thread's id is different than std::thread::id() default constructed.您始终可以检查线程的 id 是否不同于 std::thread::id() 默认构造的。 A Running thread has always a genuine associated id.一个正在运行的线程总是有一个真正的关联 id。 Try to avoid too much fancy stuff :)尽量避免太多花哨的东西:)

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);

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().要检查线程是否仍在运行,调用线程调用 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) (如果 try_lock 有效,请确保解锁互斥锁)

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.一个小问题,mutex.try_lock() 将在创建线程和锁定互斥锁之间返回 false,但可以使用稍微复杂的方法来避免这种情况。

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我检查了两个系统: - 使用线程+原子:需要 9738 毫秒 - 使用未来+异步:需要 7746 毫秒 非线程:56000 毫秒 使用 Core-I7 6 核笔记本电脑

My code creates 4000 threads, but no more than 12 running every time.我的代码创建了 4000 个线程,但每次运行不超过 12 个。

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; } }

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.尝试使用 C++20 std::jthread使用共享停止状态来检查线程是否结束,但在线程内部std::stop_token参数是只读的,并且在线程完成时不会向外部指示。

So I created a simple class ( nes::uthread ) extending std::thread with a flag to indicate it's finished.所以我创建了一个简单的类( nes::uthread ),用一个标志来扩展std::thread以表明它已经完成。 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.您可以在nes::uthread中使用std::jthread ,因此您不需要加入。

Surely have a mutex-wrapped variable initialised to false , that the thread sets to true as the last thing it does before exiting.肯定有一个互斥体包装变量初始化为false ,线程设置为true作为它退出前的最后一件事。 Is that atomic enough for your needs?原子性足以满足您的需求吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM