Подтвердить что ты не робот

Тайм-аут для thread.join()

Можно ли установить тайм-аут для вызова std::thread::join()? Я хочу обработать случай, когда поток слишком длинный для запуска или завершение потока. Я могу делать это для нескольких потоков (скажем, до 30).

Желательно без повышения, но мне было бы интересно повысить решение, если это лучший способ.

4b9b3361

Ответ 1

Нет таймаута для std::thread::join(). Однако вы можете рассматривать std::thread::join() как функцию удобства. Используя condition_variable, вы можете создать очень богатую коммуникацию и сотрудничество между вашими потоками, включая время ожидания. Например:

#include <chrono>
#include <thread>
#include <iostream>

int thread_count = 0;
bool time_to_quit = false;
std::mutex m;
std::condition_variable cv;

void f(int id)
{
    {
    std::lock_guard<std::mutex> _(m);
    ++thread_count;
    }
    while (true)
    {
        {
        std::lock_guard<std::mutex> _(m);
        std::cout << "thread " << id << " working\n";
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(250));
        std::lock_guard<std::mutex> _(m);
        if (time_to_quit)
            break;
    }
    std::lock_guard<std::mutex> _(m);
    std::cout << "thread ended\n";
    --thread_count;
    cv.notify_all();
}

int main()
{
    typedef std::chrono::steady_clock Clock;
    std::thread(f, 1).detach();
    std::thread(f, 2).detach();
    std::thread(f, 3).detach();
    std::thread(f, 4).detach();
    std::thread(f, 5).detach();
    auto t0 = Clock::now();
    auto t1 = t0 + std::chrono::seconds(5);
    std::unique_lock<std::mutex> lk(m);
    while (!time_to_quit && Clock::now() < t1)
        cv.wait_until(lk, t1);
    time_to_quit = true;
    std::cout << "main ending\n";
    while (thread_count > 0)
        cv.wait(lk);
    std::cout << "main ended\n";
}

В этом примере main запускает несколько потоков для выполнения работы, все из которых иногда проверяют, не пришло ли время выйти из мьютекса (это также может быть атомарным). Основной поток также отслеживает, пора ли уходить (если потоки выполняют всю свою работу). Если у магии заканчивается терпение, он просто объявляет, что пришло время уйти, а затем ждет, пока все потоки будут выполнять необходимую очистку до выхода.

Ответ 2

Вместо явного использования потоков вы можете использовать std::async(), чтобы предоставить вам std::future<>, и вы можете делать ожидания по времени на std::future:

http://en.cppreference.com/w/cpp/thread/future/wait_for

Ответ 3

Да, это возможно. Решение, предложенное Галиком, выглядит так:

#include <thread>
#include <future>
...
// Launch the thread.
std::thread thread(ThreadFnc, ...);
...
// Terminate the thread.
auto future = std::async(std::launch::async, &std::thread::join, &thread);
if (future.wait_for(std::chrono::seconds(5)) 
    == std::future_status::timeout) {

  /* --- Do something, if thread has not terminated within 5 s. --- */

}

Однако это, по сути, запускает третий поток, который выполняет thread.join().

(Примечание: деструктор future будет блокироваться до тех пор, пока thread не присоединится, а вспомогательный поток не будет завершен.)


Возможно, запуск потока просто для переноса другого потока - это не то, что вы хотите. Существует еще одно портативное решение без вспомогательной нити:

#include <thread>
#include <future>
...
// Launch the thread.
std::future<T_return>*  hThread 
  = new std::future<T_return>(std::async(std::launch::async, ThreadFnc, ...));
...
// Terminate the thread.
if (hThread->wait_for(std::chrono::seconds(5)) 
    == std::future_status::timeout) {

  /* --- Do something, if thread has not terminated within 5 s. --- */

} else
  delete hThread;

где T_return - тип возвращаемого потока вашей процедуры потока. Этот сценарий использует комбинацию std::future/std::async вместо std::thread.

Обратите внимание, что hThread является указателем. Когда вы вызываете на нем оператор delete, он вызывается деструктором *hThread и блокируется до тех пор, пока поток не завершится.

Я тестировал обе версии с gcc 4.9.3 на Cygwin.

Ответ 4

Для Boost см. timed_join() для версии join() с таймаутом.

Ответ 5

timed_join() теперь устарел. Вместо этого используйте try_join_for():

myThread.try_join_for(boost::chrono::milliseconds(8000))