2015-09-12 4 views
1

Теперь я ничего не понимаю. Рассмотрим У меня есть следующий фрагмент кода (упрощенный вариант):Ожидание потока с таймаутом: freeze

#include <iostream> 
#include <thread> 
#include <condition_variable> 
#include <mutex> 
#include <chrono> 

const auto k_sleep = std::chrono::seconds{3}; 
const auto k_wait = std::chrono::seconds{1}; 
const auto k_repeats = 20u; 

void test() 
{ 
    std::mutex m; 
    std::condition_variable c; 
    bool processed = false; 

    std::thread worker{[&]() { 
      std::this_thread::sleep_for(k_sleep); 

      std::unique_lock<std::mutex> lock(m); 
      processed = true; 
      lock.unlock(); 
      c.notify_one(); 
     }}; 

    std::unique_lock<std::mutex> lock(m); 
    if(c.wait_for(lock, k_wait, [&processed]() { 
      return processed; 
     })) 
    { 
     worker.join(); 
     std::cout << "done" << std::endl; 
    } 
    else 
    { 
     worker.detach(); 
     std::cout << "timeout" << std::endl; 
    } 
} 

int main() 
{ 
    for(auto i = 0u; i < k_repeats; ++i) 
     test(); 
} 

Несколько вопросов:

  • являются ли какие-либо тупики?
  • Я использую condition_variable (и все остальное по отношению к thread) правильно?
  • есть UB?
  • если все ОК, сколько раз timeout будет напечатано?

Как вы можете видеть, я запускаю поток и жду его (используя condition_variable) некоторое время. Время ожидания меньше времени выполнения потока.

С как VC++ (Visual Studio 2015, v 19.00.23026) и г ++ (ст 4.8.2) У меня есть timeout 2 раза распечатаны, а затем я застрял на worker.join() под отладчиком. Если я увеличу k_sleep до чего-то большого (относительно k_wait с небольшим количеством циклов), например, 30 секунд - все будет хорошо.

Итак, почему это происходит? Если я сделаю что-то неправильно, объясните мне правильный путь. Благодаря

+0

Вы не должны называть 'worker.detach();', но 'join()' в любом случае. –

+0

@ πάνταῥεῖ, но я не хочу ждать потока в случае таймаута. Как я могу это сделать без вызова 'detach()' on thread? – grisha

+0

@ πάνταῥεῖ, о, я понимаю - я использую 'condition_variable', который был уничтожен. Спасибо – grisha

ответ

0

У вас есть проблемы с управлением памятью там:

  1. Видеосъемка переменных по ссылке в лямбда, который передается в новый поток.
  2. Все переменные (m, c и processed) выделены в стеке, и они выйдут из области действия при отсоединении рабочего потока. Итак, когда поток проснется, он получит доступ к мусору.

Итак, вам нужно выделить данные в куче и убедиться, что вы не фиксируете переменные по ссылке. Я всегда предпочитаю передавать данные явно в std::thread.

#include <iostream> 
#include <thread> 
#include <condition_variable> 
#include <mutex> 
#include <chrono> 
#include <memory> 

const auto k_sleep = std::chrono::seconds{ 3 }; 
const auto k_wait = std::chrono::seconds{ 1 }; 
const auto k_repeats = 20u; 


struct Data 
{ 
    std::mutex m; 
    std::condition_variable c; 
    bool processed = false; 
}; 

void test() 
{ 
    auto data = std::make_shared<Data>(); 

    auto run = [](std::shared_ptr<Data> i_data) { 
     std::this_thread::sleep_for(k_sleep); 

     std::unique_lock<std::mutex> lock(i_data->m); 
     i_data->processed = true; 
     lock.unlock(); 
     i_data->c.notify_one(); 
    }; 

    std::thread worker{ run, data }; 

    std::unique_lock<std::mutex> lock(data->m); 
    if (data->c.wait_for(lock, k_wait, [&data]() { 
     return data->processed; 
    })) 
    { 
     worker.join(); 
     std::cout << "done" << std::endl; 
    } 
    else 
    { 
     worker.detach(); 
     std::cout << "timeout" << std::endl; 
    } 
} 

int main() 
{ 
    for (auto i = 0u; i < k_repeats; ++i) 
     test(); 
} 
+0

спасибо, я уже сделал одна и та же. Даже не знаю, как объяснить такую ​​легкую ошибку :) – grisha

Смежные вопросы