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

问题描述 投票:67回答:6

如何检查std::thread是否仍在运行(以独立于平台的方式)?它缺乏timed_join()方法,joinable()不是那个意思。

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

你知道更优雅的方法吗?

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

c++ multithreading c++11 stdthread
6个回答
94
投票

如果你愿意使用C ++ 11 std::asyncstd::future来运行你的任务,那么你可以利用wait_forstd::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.
}

6
投票

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

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

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

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

Edit2如果线程由于异常而退出,则有两个线程“主”函数:第一个函数有一个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;
}

3
投票

这种简单的机制可用于检测线程的完成而不会在join方法中阻塞。

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

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

1
投票

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

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


0
投票

当然有一个互斥量包装的变量初始化为false,该线程设置为true作为退出之前的最后一件事。这个原子是否足以满足您的需求?


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.