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

kis*_*ljr 76 c++ multithreading c++11 stdthread

如何检查a std::thread是否仍在运行(以独立于平台的方式)?它缺乏一种timed_join()方法,joinable()并不适用于此.

我想std::lock_guard在线程中使用a锁定互斥锁并使用try_lock()互斥锁的方法来确定它是否仍然被锁定(线程正在运行),但对我来说似乎不必要的复杂.

你知道更优雅的方法吗?

更新:要明确:我想检查线程是否干净地退出.为此,"悬挂"线程被认为正在运行.

Snp*_*nps 103

如果您愿意使用C++ 11 std::asyncstd::future运行任务,那么您可以利用wait_for函数std::future来检查线程是否仍然以这样的方式运行:

#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.
}
Run Code Online (Sandbox Code Playgroud)

如果必须使用,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.
}
Run Code Online (Sandbox Code Playgroud)

这两个例子都将输出:

Thread still running
Run Code Online (Sandbox Code Playgroud)

这当然是因为在任务完成之前检查了线程状态.

但话说回来,就像其他人已经提到的那样做起来可能更简单:

#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.
}
Run Code Online (Sandbox Code Playgroud)

编辑:

除了std::packaged_task使用之外,还可以使用std::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.
}
Run Code Online (Sandbox Code Playgroud)

  • @YagamyLight在C++中,默认情况下没有任何内容是原子的,除非它包含在`std :: atomic`中.`sizeof(bool)`是实现定义的,可能> 1,因此可能会发生部分写入.还有缓存一致性的问题.. (5认同)
  • 很好的答案.我想补充一点,它也适用于没有任何返回值的线程和未来的<void> (2认同)
  • @YagamyLight 更多信息:[我什么时候真的需要使用 atomic&lt;bool&gt; 而不是 bool?](http://stackoverflow.com/q/16320838/873025) (2认同)
  • 请注意,std::chrono_literals 需要 C++14 来编译 (2认同)

Evg*_*pov 6

您可以使用这种简单的机制来检测线程的完成情况,而不会在 join 方法中阻塞。

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

while(thread.joinable())
    sleep(1);
Run Code Online (Sandbox Code Playgroud)

  • 分离线程最终不是人们想要的,如果你不这样做,你必须从某个线程调用 join() ,该线程不等待线程释放其 joinable() 属性,否则它将循环无限地(即`joinable()`返回true,直到线程实际上被`join()`ed,而不是直到它完成为止) (2认同)
  • 这种调用分离的方法不需要互斥锁和其他更复杂的解决方案。我使用它并且有效!感谢你的回答。 (2认同)

Som*_*ude 5

一个简单的解决方案是有一个布尔变量,线程在定期的时间间隔内设置为true,并且由想要知道状态的线程检查并设置为false.如果变量为false,则该线程不再被视为活动状态.

一种更安全的方法是让一个由子线程增加的计数器,并且主线程将计数器与存储的值进行比较,如果在很长时间后相同,那么子线程被认为是不活动的.

但请注意,C++ 11中没有办法实际杀死或删除已挂起的线程.

编辑如何检查线程是否已完全退出:基本上与第一段中描述的技术相同; 将布尔变量初始化为false.子线程执行的最后一件事是将其设置为true.然后主线程可以检查该变量,如果为true则在子线程上进行连接而没有太多(如果有的话)阻塞.

Edit2如果线程由于异常而退出,则有两个线程"main"函数:第一个函数有一个try- catch在其中调用第二个"真正的"主线程函数.第一个main函数设置"have_exited"变量.像这样的东西:

bool thread_done = false;

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

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

    thread_done = true;

    return res;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果不同的线程正在读写`thread_done`,则此代码在没有内存屏障的情况下被破坏.请改用`std :: atomic <bool>`. (6认同)
  • 查看[此问题](http://stackoverflow.com/questions/14365595/is-it-necessary-to-use-a-stdatomic-to-signal-that-a-thread-has-finished-execut)讨论为什么这里需要`std :: atomic <bool>`. (3认同)

Mic*_*lik 5

您始终可以检查线程的 id 是否与 std::thread::id() 默认构造的不同。正在运行的线程始终具有真正的关联 ID。尽量避免太多花哨的东西:)