Как проверить, работает ли std:: thread?

как я могу проверить, если std::thread все еще работает (независимо от платформы)? Ему не хватает timed_join() способ и joinable() не предназначен для этого.

Я думал о блокировке мьютекса с std::lock_guard в потоке и с помощью try_lock() метод мьютекса, чтобы определить, по-прежнему ли он заблокирован (поток запущен), но мне кажется излишне сложным.

знаете ли вы более элегантный метод?

обновление: для ясности: я хочу проверьте, вышел ли поток чисто или нет. Для этой цели считается "висячая" нить.

5 ответов


если вы готовы использовать C++11 std::async и std::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.
}

Edit:

есть еще 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.
}

простым решением является наличие булевой переменной, которую поток устанавливает в true через регулярные интервалы, и которая проверяется и устанавливается в false потоком, желающим знать состояние. Если переменная имеет значение false for to long, поток больше не считается активным.

более потокобезопасным способом является наличие счетчика, который увеличивается дочерним потоком, а основной поток сравнивает счетчик с сохраненным значением, и если то же самое после слишком долгого времени, то дочерний поток считается неактивным.

Примечание однако в C++11 нет способа фактически убить или удалить поток, который повесился.

редактировать Как проверить, вышел ли поток чисто или нет: в основном тот же метод, что описан в первом абзаце; имеют логическую переменную, инициализированную false. Последнее, что делает дочерний поток, это устанавливает его в true. Затем основной поток может проверить эту переменную, и если true, то соедините дочерний поток без особых усилий (если любой) блокировка.

Edit2 если поток выходит из-за исключения, то есть две "основные" функции потока: первая имеет try -catch внутри которого он вызывает вторую" реальную " функцию основного потока. Эта первая основная функция устанавливает переменную "have_exited". Что-то вроде этого:--4-->

bool thread_done = false;

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

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

    thread_done = true;

    return res;
}

этот простой механизм можно использовать для обнаружения окончания потока без блокировки в методе соединения.

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

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

создайте мьютекс, к которому имеют доступ и текущий поток, и вызывающий поток. Когда запущенный поток запускается, он блокирует мьютекс, а когда он заканчивается, он разблокирует мьютекс. Чтобы проверить, работает ли поток, вызывающий поток вызывает мьютекс.try_lock(). Возвращаемое значение этого-состояние потока. (Просто убедитесь, что разблокировать мьютекс, если try_lock работал)

одна небольшая проблема с этим, мьютекс.try_lock () вернет false между временем потока создан, и когда он блокирует мьютекс, но этого можно избежать с помощью немного более сложного метода.


конечно, есть переменная, обернутая мьютексом, инициализированная false, что поток устанавливается в true как последнее, что он делает перед выходом. Этого достаточно для ваших нужд?