2016-03-06 219 views
0

我想在C++ 11中實現一個小的看門狗定時器類,它應該在過期時調用一些代碼。在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毫秒),是否有更好的方法來實現此功能?

+0

而是使用條件變量並等待它發送信號的某個時間。 –

+0

我不明白,爲什麼你不只是'std :: this_thread :: sleep_for(std :: chrono :: milliseconds(_interval));'? –

+1

另外,你的'Start'方法可以啓動多個線程,在檢查'_running'並設置它之間有四行。應該原子地做它http://en.cppreference.com/w/cpp/atomic/atomic_flag_test_and_set 例如:'if(_running.test_and_set(_running))' –

回答

0

似乎有趣,我可以拿出這段代碼。 它不編譯,你必須在它上面工作很多,但它顯示了一個關於如何去做的想法。

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

0

這應該工作。有兩個類真的。一個用來處理通知(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; 
    }