Как проверить, работает ли std :: thread? - PullRequest
69 голосов
/ 01 февраля 2012

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

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

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

Обновление: Для ясности: я хочу проверить, правильно ли завершился поток или нет. С этой целью считается «висящая» нить.

Ответы [ 6 ]

94 голосов
/ 20 июня 2013

Если вы хотите использовать 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.
}

Редактировать:

Также есть 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 голосов
/ 01 февраля 2012

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

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

Обратите внимание, однако, что в 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;
}
3 голосов
/ 25 марта 2016

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

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

while(thread.joinable())
    sleep(1);
1 голос
/ 19 июня 2013

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

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

0 голосов
/ 25 марта 2019

Вы всегда можете проверить, отличается ли идентификатор потока от стандартного std :: thread :: id (). У запущенного потока всегда есть подлинный связанный идентификатор. Старайтесь избегать слишком много модных вещей:)

0 голосов
/ 01 февраля 2012

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

...