2014-02-03 4 views
5

Я пытаюсь реализовать базовый таймер с классическими методами: start() и stop(). Я использую C++ 11 с std :: thread и std :: chrono.Базовый таймер с std :: thread и std :: chrono

  • Старт метод. Создает новый поток, который спит в течение заданного интервала времени, затем выполняет заданную функцию std ::. Этот процесс повторяется, пока флаг «running» равен true.
  • Остановить метод. Просто устанавливает флаг «running» равным false.

Я создал и запустил объект Timer, который показывает «Привет!». каждую секунду, затем с другим потоком я пытаюсь остановить таймер, но я не могу. Таймер никогда не останавливается.

Я думаю, проблема связана с th.join() [*], которая прекращает выполнение до тех пор, пока поток не завершится, но когда я удаляю строку th.join(), очевидно, что программа заканчивается до начала отсчета таймера.

Итак, мой вопрос заключается в том, как запустить поток без остановки других потоков?

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

using namespace std; 

class Timer 
{ 
    thread th; 
    bool running = false; 

public: 
    typedef std::chrono::milliseconds Interval; 
    typedef std::function<void(void)> Timeout; 

    void start(const Interval &interval, 
       const Timeout &timeout) 
    { 
     running = true; 

     th = thread([=]() 
     { 
      while (running == true) { 
       this_thread::sleep_for(interval); 
       timeout(); 
      } 
     }); 

// [*] 
     th.join(); 
    } 

    void stop() 
    { 
     running = false; 
    } 
}; 

int main(void) 
{ 
    Timer tHello; 
    tHello.start(chrono::milliseconds(1000), 
       []() 
    { 
     cout << "Hello!" << endl; 
    }); 

    thread th([&]() 
    { 
     this_thread::sleep_for(chrono::seconds(2)); 
     tHello.stop(); 
    }); 

    th.join(); 

    return 0; 
} 

Выход:

Hello! 
Hello! 
... 
... 
... 
Hello! 
+0

Как побочный комментарий, в моем опыте, пытающемся сделать то же самое, использование condition_variable было более эффективным (потому что оно предназначено для такого рода вещей). Однако это делает код менее ясным. – Klaim

ответ

7

В Timer::start, вы создаете новый поток в th, а затем сразу join его с th.join(). Фактически, start не вернется, пока этот порожденный поток не выйдет. Конечно, он никогда не выйдет из-за того, что ничего не изменится до false до тех пор, пока не вернется start ...

Не пропустите нитку, пока вы не намереваетесь дождаться ее завершения. В этом случае в stop после установки running = false, вероятно, является правильным местом.

Также - хотя это неверно - нет необходимости в создании другой нити в main для звонка this_thread::sleep_for. Вы можете просто сделать это с основной нитью:

int main() 
{ 
    Timer tHello; 
    tHello.start(chrono::milliseconds(1000), []{ 
     cout << "Hello!" << endl; 
    }); 

    this_thread::sleep_for(chrono::seconds(2)); 
    tHello.stop(); 
} 
1

Вместо размещения join в start место это после того, как running = false в stop. Тогда метод остановки будет эффективно ждать завершения потока до его возвращения.

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