98

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

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

你知道更优雅的方法吗?

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

4

7 回答 7

135

如果您愿意使用 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_taskforstd::thread用于比 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.
}
于 2013-06-20T02:11:58.553 回答
5

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

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

但是请注意,在 C++11 中无法真正杀死或删除已挂起的线程。

编辑如何检查线程是否干净退出:基本上与第一段中描述的技术相同;将布尔变量初始化为 false。子线程做的最后一件事是将其设置为 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;
}
于 2012-02-01T10:47:51.803 回答
5

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

于 2019-03-25T12:57:47.433 回答
4

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

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

while(thread.joinable())
    sleep(1);
于 2016-03-25T17:11:29.853 回答
1

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

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

于 2013-06-19T11:21:11.660 回答
0

肯定有一个已初始化为 的互斥体包装变量false,线程将其设置true为它在退出之前所做的最后一件事。原子性足以满足您的需求吗?

于 2012-02-01T11:19:22.960 回答
0

我检查了两个系统: - 使用线程+原子:花费 9738 毫秒 -使用未来 + 异步:花费 7746 毫秒 非线程:56000 毫秒 使用 Core-I7 6 核笔记本电脑

我的代码创建了 4000 个线程,但每次运行不超过 12 个。

这是代码:

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

于 2021-09-09T16:32:22.053 回答