Я пытаюсь реализовать небольшой класс сторожевого таймера в C ++ 11, который должен вызывать некоторый код, когда он истекает.
Watchdog.h:
#pragma once
#include <thread>
#include <atomic>
class Watchdog
{
public:
Watchdog();
Watchdog(unsigned int milliseconds, std::function<void()> callback);
~Watchdog();
void Start(unsigned int milliseconds, std::function<void()> callback);
void Stop();
void Pet();
private:
unsigned int _interval;
std::atomic<unsigned int> _timer;
std::atomic<bool> _running;
std::thread _thread;
std::function<void()> _callback;
void Loop();
};
Watchdog.cpp:
#include "Watchdog.h"
Watchdog::Watchdog() :
_interval(0),
_timer(0),
_running(false)
{
}
Watchdog::Watchdog(unsigned int milliseconds, std::function<void()> callback)
{
Start(milliseconds, callback);
}
Watchdog::~Watchdog()
{
}
void Watchdog::Start(unsigned int milliseconds, std::function<void()> callback)
{
_interval = milliseconds;
_timer = 0;
_callback = callback;
_running = true;
_thread = std::thread(&Watchdog::Loop, this);
}
void Watchdog::Stop()
{
_running = false;
_thread.join();
}
void Watchdog::Pet()
{
_timer = 0;
}
void Watchdog::Loop()
{
while (_running)
{
_timer++;
if (_timer >= _interval)
{
_running = false;
_callback();
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
Тем не менее, этот цикл потока кажется мне немного грязным, и std::this_thread::sleep_for
не является точным (спит для по крайней мере указанная сумма, то есть она может быть длиннее 1 мс), есть ли лучший способ для достижения этой функции?
Кажется интересным, я мог бы придумать этот код.
Он не компилируется, и вам нужно много работать над этим, но он показывает одну идею о том, как это сделать.
std::mutex cmutex; // needed for the condition_variable
std::condition_variable stop_condition;
std::chrono::time_point last_pet_time;
void Watchdog::Start(unsigned int milliseconds, std::function<void()> callback)
{
// somewhere in this method:
last_pet_time = now();
timeout = milliseconds;
}void Watchdog::Stop()
{
if (_running) {
std::unique_lock<std::mutex> lock(cmutex);
_running = false;
stop_condition.notify_all(); // tell Loop() to stop
_thread.join();
}
}
void Watchdog::Pet()
{
std::unique_lock<std::mutex> lock(cmutex);
last_pet_time = now();
}void Watchdog::Loop()
{
std::unique_lock<std::mutex> lock(cmutex);
while (_running // was Stop() called?
and (now() - last_pet_time) < timeout) // was Pet() ( or Start() ) called recently?
{
// here the threads waits until:
// 1. the condition_variable is notified in ::Stop()
// 2. or the timeout expires
// 3. or until spurious wakeup
stop_condition.wait_for(lock, timeout);
}
if (_running) {
_running = false;
callback();
}
}
Проверьте: http://en.cppreference.com/w/cpp/thread/condition_variable/wait_until
Это должно работать. На самом деле есть два класса. Один для обработки уведомления (AutoResetEvent) и сам класс Watchdog.
AutoResetEvent.h
#pragma once
#include <mutex>
class AutoResetEvent
{
private:
bool m_ready = true;
std::condition_variable m_condition;
std::mutex m_mutex;
public:
AutoResetEvent();
~AutoResetEvent();
void WaitOne();
void WaitFor(unsigned __int32 milli_secs);
void Notify();
};
AutoResetEvent.cpp
AutoResetEvent::AutoResetEvent()
{
}AutoResetEvent::~AutoResetEvent()
{
Notify();
}
void AutoResetEvent::Notify()
{
if (!m_ready)
{
std::unique_lock<std::mutex> locker(m_mutex);
m_ready = true;
m_condition.notify_all();
}
}
void AutoResetEvent::WaitOne()
{
std::unique_lock<std::mutex> locker(m_mutex);
m_ready = false;
m_condition.wait(locker, [&ready = m_ready]() {return ready; });
}
void AutoResetEvent::WaitFor(unsigned __int32 milli_secs)
{
std::unique_lock<std::mutex> locker(m_mutex);
m_ready = false;
m_condition.wait_for(locker, std::chrono::milliseconds(milli_secs), [&ready = m_ready]() {return ready; });
}
Watchdog.h
#pragma once
#include "AutoResetEvent.h"#include <functional>
class Watchdog
{
private:
const unsigned __int32 WATCHDOG_BEEP_MS = 5*60*1000;
bool m_active_status = false;
bool m_new_pet = false;
bool m_cancelled = false;
AutoResetEvent m_reset_event;
std::function<void(void)> m_call_back;
void Loop();
public:
Watchdog(std::function<void(void)> call_back);
~Watchdog();
bool Start();
void Pet();
void Stop();
bool IsActive() const;
};
Watchdog.cpp
#include "stdafx.h"#include "Watchdog.h"
void Watchdog::Loop()
{
m_active_status = true;
m_cancelled = false;
while (true)
{
m_new_pet = false;
m_reset_event.WaitFor(WATCHDOG_BEEP_MS);
if (!m_new_pet && !m_cancelled)
{
m_call_back();
break;
}
if (m_cancelled)
{
break;
}
}
m_active_status = false;
}
Watchdog::Watchdog(std::function<void(void)> call_back): m_call_back(call_back)
{
}
Watchdog::~Watchdog()
{
Stop();
}
bool Watchdog::Start()
{
if (IsActive()) return false;
std::thread loop_thread(&Watchdog::Loop, this);
loop_thread.detach();
return true;
}void Watchdog::Pet()
{
if (!IsActive()) return;
m_new_pet = true;
m_reset_event.Notify();
}
void Watchdog::Stop()
{
if (!IsActive()) return;
m_cancelled = true;
m_reset_event.Notify();
}
bool Watchdog::IsActive() const
{
return m_active_status;
}