2015-03-05 3 views
0

Я сделал очень простой класс таймера с таймером и дал подводные камни вокруг кода МТ, я бы хотел проверить здравый смысл. Идея здесь состоит в том, чтобы начать поток, а затем непрерывно ждать цикла в переменной. Если время ожидания закончилось, интервал был превышен, и мы вызываем обратный вызов. Если переменная была указана, поток должен выйти, и мы не вызываем обратный вызов.Простой резьбовой таймер, проверка работоспособности

Одна из вещей, о которых я не уверен, - это то, что происходит в деструкторе с моим кодом, поскольку поток может быть соединен там (просто). Могу ли я присоединиться к потоку в деструкторе, чтобы убедиться, что он закончен?

Вот класс:

class TimerThreaded 
{ 
public: 

    TimerThreaded() {} 
    ~TimerThreaded() 
    {   
     if (MyThread.joinable()) 
      Stop();    
    } 

    void Start(std::chrono::milliseconds const & interval, std::function<void(void)> const & callback) 
    { 
     if (MyThread.joinable()) 
      Stop(); 

     MyThread = std::thread([=]() 
     { 
      for (;;) 
      { 
       auto locked = std::unique_lock<std::mutex>(MyMutex); 
       auto result = MyTerminate.wait_for(locked, interval); 

       if (result == std::cv_status::timeout) 
        callback(); 
       else 
        return; 
      } 
     }); 
    } 

    void Stop() 
    { 
     MyTerminate.notify_all(); 
    } 

private: 

    std::thread MyThread; 
    std::mutex MyMutex; 
    std::condition_variable MyTerminate; 
}; 

Я полагаю, лучше вопрос может быть попросить кого-нибудь мне точку к очень простому древовидному таймеру, если есть один уже есть где-то.

ответ

2

Могу ли я присоединиться к потоку деструктора, чтобы убедиться, что он закончен?

Не только вы можете, но это вполне типично для этого. Если экземпляр потока может быть соединен (т. Е. Все еще запущен), когда он будет уничтожен, будет вызван terminate.

По какой-либо причине результат всегда является таймаутом. Кажется, он никогда не сигнализируется и никогда не останавливается. Правильно ли это? notify_all должен разблокировать wait_for?

Он может быть разблокирован, если нить в данный момент находится на cv. То, что вы, вероятно, делаете, это позвонить Start, а затем сразу Stop до того, как поток начал работать и начал ждать (или, возможно, пока работает callback). В этом случае нить никогда не будет уведомлена.

Есть еще одна проблема с вашим кодом. Заблокированные потоки могут быть ложно проснуты на некоторых реализациях, даже если вы явно не вызываете notify_X. Это заставит ваш таймер останавливаться случайным образом без видимых причин.

Я предлагаю вам добавить переменную флага, которая указывает, был ли вызван Stop. Это устранит обе указанные проблемы. Это типичный способ использования переменных условия. Я даже написал код для вас:

class TimerThreaded 
{ 
... 
     MyThread = std::thread([=]() 
     { 
      for (;;) 
      { 
       auto locked = std::unique_lock<std::mutex>(MyMutex); 
       auto result = MyTerminate.wait_for(locked, interval); 

       if (stop_please) 
        return; 
       if (result == std::cv_status::timeout) 
        callback(); 
      } 
     }); 
.... 
    void Stop() 
    { 
     { 
      std::lock_guard<std::mutex> lock(MyMutex); 
      stop_please = true; 
     } 
     MyTerminate.notify_all(); 
     MyThread.join(); 
    } 

... 
private: 
    bool stop_please = false; 
... 

С этими изменениями Yout таймер должен работать, но не понимают, что «[std::condition_variable::wait_for] может блокировать дольше timeout_duration за счет планирования или раздора ресурсов задержки», в слова cppreference.com.

Направьте меня на очень простой резьбовой таймер, если он уже имеется где-то.

Я не знаю стандартного C++-решения, но современные операционные системы обычно предоставляют такую ​​функциональность или, по крайней мере, части, которые могут быть использованы для ее создания. См., Например, timerfd_create на linux.

+0

Чтобы быть точным, вызывается 'terminate'. Никакие исключения не выбрасываются. – Damon

+0

@ Дамон ах, ты прав. Исправлена. – user2079303

+0

Кажется, что это не работает. По какой-то причине результатом всегда является тайм-аут.Кажется, он никогда не сигнализируется и никогда не останавливается. Правильно ли это? notify_all должен разблокировать wait_for? – Robinson

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