2015-04-21 5 views
12

Предположим, у меня есть поток, который должен выполнять какую-то задачу периодически, но этот период равен 6 раз в час 12 раз в час (каждые 5 минут), я часто видел код, который контролирует поток петля с is_running флаг, который проверяется каждый цикл, как это:Остановка длинноволновых потоков

std::atomic<bool> is_running; 

void start() 
{ 
    is_running.store(true); 
    std::thread { thread_function }.detach(); 
} 

void stop() 
{ 
    is_running.store(false); 
} 

void thread_function() 
{ 
    using namespace std::literals; 
    while (is_running.load()) 
    { 
     // do some task... 
     std::this_thread::sleep_for(5min); 
    } 
} 

Но если функция stop() называется, скажем, 1 миллисекунду после start() нить будет в живых 299999 дополнительных миллисекунд, пока не будит , проверяет флаг и умирает.

Правильно ли я понимаю? Как избежать сохранения (но спящего) потока, который должен был быть закончен? Мой лучший подход до сих пор является следующим:

void thread_function() 
{ 
    using namespace std::literals; 
    while (is_running.load()) 
    { 
     // do some task... 
     for (unsigned int b = 0u, e = 1500u; is_running.load() && (b != e); ++b) 
     { 
      // 1500 * 200 = 300000ms = 5min 
      std::this_thread::sleep_for(200ms); 
     } 
    } 
} 

Есть ли менее грязный и более простой способ для достижения этой цели?

+1

_6 раз каждый час (каждые 5 минут) _, хорошо 12 раз в час или каждые 10 минут? :) –

+0

@AlexandreLavoie какой провал! Спасибо, я исправлю! :) –

+3

http://en.cppreference.com/w/cpp/thread/condition_variable, см. Первое предложение там. Вместо того, чтобы спать в течение фиксированного времени, вы вводите условие ожидания для этого количества времени, чтобы другие потоки все равно могли прерывать вас. – stijn

ответ

12

Используйте переменную условия. Вы ждете от переменной состояния или Проходит 5 минут. Не забудьте проверить наличие побочных пробуждений.

cppreference

Я не могу найти хороший стек переполнения поста о том, как использовать переменное условие в минуте или две Google поиска. Трудная часть понимает, что wait может просыпаться без прохождения ни минуты 5 минут, ни посылаемого сигнала. Самый чистый способ справиться с этим - использовать методы wait с лямбдой, которая проверяет, что пробуждение является «хорошим».

here - это пример кода на cppreference, который использует wait_until с лямбдой. (wait_for с лямбдой эквивалент wait_until с лямбдой). Я немного изменил его.

Вот версия:

struct timer_killer { 
    // returns false if killed: 
    template<class R, class P> 
    bool wait_for(std::chrono::duration<R,P> const& time) const { 
    std::unique_lock<std::mutex> lock(m); 
    return !cv.wait_for(lock, time, [&]{return terminate;}); 
    } 
    void kill() { 
    { 
     std::unique_lock<std::mutex> lock(m); 
     terminate=true; // should be modified inside mutex lock 
    } 
    cv.notify_all(); // it is safe, and *sometimes* optimal, to do this outside the lock 
    } 
    // I like to explicitly delete/default special member functions: 
    timer_killer() = default; 
    timer_killer(timer_killer&&)=delete; 
    timer_killer(timer_killer const&)=delete; 
    timer_killer& operator=(timer_killer&&)=delete; 
    timer_killer& operator=(timer_killer const&)=delete; 
private: 
    std::condition_variable cv; 
    mutable std::mutex m; 
    bool terminate = false; 
}; 

live example.

Вы создаете timer_killer в общем месте. Потоки клиентов могут быть wait_for(time). Если он возвращает false, это означает, что вы были убиты до того, как ваше ожидание закончилось.

Контрольная нить только вызывает kill(), и все, кто делает wait_for, получают false.

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

-1

да, по средствам std::mutex, std::lock_guard и std::conditional_variable:

std::mutex mtx; 
std::conditional_variable cv; 

void someThreadFunction(){ 
    while(!stopThreadFlag){ 
    std::lock_gurad<std::mutex> lg(mtx); 
    cv.wait_for(lg,SOME_ITERVAL,!stopThreadFlag || wakeTheThreadVariable); 
    //the rest here 
    } 
} 
+0

Ваш код не обрабатывает ложные пробуждения. – Yakk

+1

Это не скомпилировалось. – Barry

+0

включили ли вы все правильные заголовки? –

2

Там два традиционных способа вы могли бы сделать это.

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

В качестве альтернативы вы могли бы poll на трубе с вашим сном в качестве таймаута вместо сна. Затем вы просто пишете байт в трубе, и поток просыпается и может выйти.

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