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


84

如何检查astd::thread是否仍在运行(以独立于平台的方式)?它缺少一种timed_join()方法,joinable()并不意味着要这样做。

我曾考虑过std::lock_guard在线程中使用a锁定互斥锁,并使用try_lock()互斥锁的方法来确定它是否仍处于锁定状态(线程正在运行),但是这对我来说似乎不必要。

您知道更优雅的方法吗?

更新:要明确:我想检查线程是否干净退出。为此,一个“挂起”线程被认为正在运行。


我猜想检查线程是否仍在运行仅在您期望时才重要wait();如果是,则在wait()尚未运行时,它必须按照定义运行。但是这种推理可能是不准确的。
ereOn'2

实际上,我有一个在特殊情况下退出的线程,我想从主线程检查它是否仍在运行,但不想等待(加入)它
kispaljr 2012年

1
跑步到底是什么意思?您是说它正在积极处理而不是处于等待状态,还是您说该线程仍然存在并且尚未终止?
CashCow 2012年

您可以随时使用boost :)
CashCow'2

4
如果您不满意答案,则不应该接受。
Nicol Bolas'2

Answers:


116

如果您愿意使用C ++ 11std::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.
}

如果必须使用,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.
}

这两个示例将输出:

Thread still running

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

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

#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.
}

编辑:

另外还有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.
}

2
好答案。我要补充一点,它也可以用于没有任何返回值和future <void>的线程
kispaljr

产生此代码的原因是什么std::atomic<bool> done(false);bool默认不是原子的吗?
Hi-Angel

6
@YagamyLight在C ++中,默认情况下,没有什么是原子的,除非将其包装在std::atomicsizeof(bool)是实现定义的值,可能大于1,所以有可能发生部分写入。也存在缓存一致性的问题。
Snps

2
@YagamyLight这里的更多信息:我何时真正需要使用atomic <bool>代替bool?
SNPS

1
请注意,std :: chrono_literals将需要C ++ 14进行编译
Patrizio Bertoni

6

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

一种更安全的线程方式是拥有一个由子线程增加的计数器,并且主线程将计数器与所存储的值进行比较;如果时间过长,则该计数器将被视为未激活。

但是请注意,C ++ 11中没有任何方法可以真正杀死或删除已挂起的线程。

编辑如何检查线程是否干净退出:基本上与第一段中所述的技术相同;将布尔变量初始化为false。子线程所做的最后一件事就是将其设置为true。然后,主线程可以检查该变量,如果为true,则在没有太多(如果有)阻塞的情况下在子线程上进行联接。

Edit2如果线程由于异常而退出,则有两个线程“主”函数:第一个具有try-的catch内部,它调用第二个“真实”主线程函数。第一个主函数设置“ 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;
}

1
如果这是OP对“运行”的定义。
CashCow 2012年

也许你误会了我。我想检查线程是否干净退出。对不起,措辞不清楚。
kispaljr'2

7
如果不同的线程正在读取和写入thread_done,则此代码将被破坏而没有内存障碍。使用std::atomic<bool>代替。
ildjarn

1
我不是在指多个工作线程,而是bool在主线程从中读取单个工作线程时要写的,这需要一个内存屏障。
ildjarn

3
查看此问题以讨论为什么在std::atomic<bool>这里需要a 。
罗伯特·鲁格2013年

3

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

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

while(thread.joinable())
    sleep(1);

2
分离线程最终不是人们想要的,如果不需要,则必须join()从某个不等待该线程释放其joinable()属性的线程中进行调用,否则它将无限循环(即,joinable()返回true,直到该线程实际处于join()ed直到完成为止)
Niklas R

joinable表示线程正在持有线程句柄。如果线程完成,它将仍然可以连接。如果您需要检查而不必等待线程结束,这是解决方案。只是几行代码。为什么不先尝试呢?
Evgeny Karpov

我做到了,而我想说的是,如果您删除该thread.detach()零件,则上面的程序将永远不会终止。
Niklas R

是的,不会。这就是为什么它最终要求分离。
Evgeny Karpov

1
这种调用detach的方法省去了对互斥锁和其他更复杂解决方案的需求。我使用它,并且有效!谢谢你的回答。
2016

1

创建一个互斥量,正在运行的线程和调用线程都可以访问。当运行线程启动时,它将锁定互斥锁,当线程结束时,它将解锁互斥锁。要检查线程是否仍在运行,调用线程将调用mutex.try_lock()。那的返回值是线程的状态。(如果try_lock有效,请确保解锁互斥锁)

与此相关的一个小问题是,muttex.try_lock()在创建线程与锁定互斥锁之间将返回false,但是可以使用稍微复杂一些的方法来避免这种情况。


-1您不应将std::mutex这种信号用于(主要是由于通常实现互斥锁的原因)。atomic_flag在这种情况下,an的效果也很好,而开销却更少。Astd::future可能会更好,因为它可以更清楚地表达意图。另外请记住,try_lock可能会意外失败,所以回报是不是一定是线程的状态(虽然它可能不会太厉害了在这种特殊情况下)。
ComicSansMS 2013年


0

当然,必须将互斥变量包装的变量初始化为false,该线程true在退出之前将其设置为最后要做的事情。这足以满足您的需求吗?


1
如果仍然使用互斥锁,那么我的解决方案(仅使用互斥锁,不带布尔值)会更优雅。如果您绝对想使用线程安全的布尔值,我建议使用std :: atomic <bool>代替。在大多数实现中,它将是无锁的。
kispaljr'2

为什么要锁定?一个线程只能读取,一个线程只能写入。在任何情况下,字长写都是原子的。
Xeo 2012年

1
@Xeo:写入可能是原子的,但是如果您希望在不同的线程(可能在不同的CPU上执行)中看到写入的值,则仍然需要内存屏障。std::atomic<bool>负责本作的你,这就是为什么它是真正的答案海事组织。
ildjarn
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.