我可以使用 Boost Signals2 和 Threads 在 C++ 中创建软件看门狗定时器线程吗?

Posted

技术标签:

【中文标题】我可以使用 Boost Signals2 和 Threads 在 C++ 中创建软件看门狗定时器线程吗?【英文标题】:Can I create a software watchdog timer thread in C++ using Boost Signals2 and Threads? 【发布时间】:2010-11-04 04:03:47 【问题描述】:

我目前正在单线程应用程序中从其他人的库中运行函数 Foo。大多数时候,我打电话给 Foo,它真的很快,有时,我打电话给 Foo,它需要很长时间。我不是一个有耐心的人,如果 Foo 要永远占用,我想停止执行 Foo 并且不使用这些参数调用它。

以受控方式调用 Foo 的最佳方法是什么(我当前的环境是 POSIX/C++),这样我可以在一定秒数后停止执行。我觉得在这里做的正确的事情是创建第二个线程来调用 Foo,而在我的主线程中我创建了一个计时器函数,如果第二个线程超时,它最终会发出信号。

还有其他更合适的模型(和解决方案)吗?如果没有,Boost 的 Signals2 库和 Threads 能解决问题吗?

【问题讨论】:

【参考方案1】:

您可以在超时的第二个线程上调用 Foo。例如:

#include <boost/date_time.hpp> 
#include <boost/thread/thread.hpp>

boost::posix_time::time_duration timeout = boost::posix_time::milliseconds(500);
boost::thread thrd(&Foo);

if (thrd.timed_join(timeout))

  //finished

else

  //Not finished;

【讨论】:

澄清一下,timed_join 不会 如果达到超时,则停止 Foo() 线程的执行,正如我认为 Aron 所要求的那样。相反,调用者只会知道在达到超时时Foo() 线程仍在运行。【参考方案2】:

您可以使用以下类:

class timer

    typedef boost::signals2::signal<void ()> timeout_slot;
public:
    typedef timeout_slot::slot_type timeout_slot_t;

public:
    timer() : _interval(0), _is_active(false) ;
    timer(int interval) : _interval(interval), _is_active(false) ;
    virtual ~timer()  stop(); ;

    inline boost::signals2::connection connect(const timeout_slot_t& subscriber)  return _signalTimeout.connect(subscriber); ;

    void start()
    
        boost::lock_guard<boost::mutex> lock(_guard);

        if (is_active())
            return; // Already executed.
        if (_interval <= 0)
            return;

        _timer_thread.interrupt();
        _timer_thread.join();

        timer_worker job;
        _timer_thread = boost::thread(job, this);

        _is_active = true;
    ;

    void stop()
    
        boost::lock_guard<boost::mutex> lock(_guard);

        if (!is_active())
            return; // Already executed.

        _timer_thread.interrupt();
        _timer_thread.join();

        _is_active = false;
    ;

    inline bool is_active() const  return _is_active; ;

    inline int get_interval() const  return _interval; ;

    void set_interval(const int msec)
    
        if (msec <= 0 || _interval == msec)
            return;

        boost::lock_guard<boost::mutex> lock(_guard);
        // Keep timer activity status.
        bool was_active = is_active();

        if (was_active)
            stop();
        // Initialize timer with new interval.
        _interval = msec;

        if (was_active)
            start();
    ;

protected:
    friend struct timer_worker;
    // The timer worker thread.
    struct timer_worker
    
        void operator()(timer* t)
        
            boost::posix_time::milliseconds duration(t->get_interval());

            try
            
                while (1)
                
                    boost::this_thread::sleep<boost::posix_time::milliseconds>(duration);
                    
                        boost::this_thread::disable_interruption di;
                        
                            t->_signalTimeout();
                        
                    
                
            
            catch (boost::thread_interrupted const& )
            
                // Handle the thread interruption exception.
                // This exception raises on boots::this_thread::interrupt.
            
        ;
    ;

protected:
    int             _interval;
    bool            _is_active;

    boost::mutex    _guard;
    boost::thread   _timer_thread;

    // Signal slots
    timeout_slot    _signalTimeout;
;

使用示例:

void _test_timer_handler()

    std::cout << "_test_timer_handler\n";


BOOST_AUTO_TEST_CASE( test_timer )

    emtorrus::timer timer;

    BOOST_CHECK(!timer.is_active());
    BOOST_CHECK(timer.get_interval() == 0);

    timer.set_interval(1000);
    timer.connect(_test_timer_handler);

    timer.start();

    BOOST_CHECK(timer.is_active());

    std::cout << "timer test started\n";

    boost::this_thread::sleep<boost::posix_time::milliseconds>(boost::posix_time::milliseconds(5500));

    timer.stop();

    BOOST_CHECK(!timer.is_active());
    BOOST_CHECK(_test_timer_count == 5);

【讨论】:

【参考方案3】:

您还可以在调用该函数之前设置警报,并捕获 SIGALRM。

【讨论】:

【参考方案4】:

弗拉德,出色的帖子!您的代码已编译并且运行良好。我用它实现了一个软件看门狗定时器。我做了一些修改:

为防止指针衰减,请将信号存储在 boost::shared_ptr 中,并将其传递给线程工作者,而不是指向计时器类的弱指针。这消除了线程工作者成为友元结构的需要,并保证信号在内存中。 添加参数 _is_periodic 以允许调用者选择工作线程是周期性的还是到期后终止。 将 _is_active、_interval 和 _is_periodic 存储在 boost::atomic 中以允许线程安全访问。 缩小互斥锁的范围。 添加 reset() 方法以“启动”计时器,防止其发出到期信号。

应用了这些更改:

#include <atomic>
#include <boost/signals2.hpp>
#include <boost/thread.hpp>

class IntervalThread

    using interval_signal = boost::signals2::signal<void(void)>;

public:
    using interval_slot_t = interval_signal::slot_type;

    IntervalThread(const int interval_ms = 60)
      : _interval_ms(interval_ms),
        _is_active(false),
        _is_periodic(false),
        _signal_expired(new interval_signal()) ;

    inline ~IntervalThread(void)  stop(); ;

    boost::signals2::connection connect(const interval_slot_t &subscriber)
    
        // thread-safe: signals2 obtains a mutex on connect()
        return _signal_expired->connect(subscriber); 
    ;

    void start(void)
    
        if (is_active())
            return; // Already executed.
        if (get_interval_ms() <= 0)
            return;

        boost::lock_guard<boost::mutex> lock(_timer_thread_guard);
        _timer_thread.interrupt();
        _timer_thread.join();

        _timer_thread = boost::thread(timer_worker(),
                static_cast<int>(get_interval_ms()),
                static_cast<bool>(is_periodic()),
                _signal_expired);
        _is_active = true;
    ;

    void reset(void)
    
        if (is_active())
            stop();
        start();
    

    void stop(void)
    
        if (!is_active())
            return; // Already executed.

        boost::lock_guard<boost::mutex> lock(_timer_thread_guard);
        _timer_thread.interrupt();
        _timer_thread.join();
        _is_active = false;
    ;

    inline bool is_active(void) const  return _is_active; ;

    inline int get_interval_ms(void) const  return _interval_ms; ;

    void set_interval_ms(const int interval_ms)
    
        if (interval_ms <= 0 || get_interval_ms() == interval_ms)
            return;

        // Cache timer activity state.
        const bool was_active = is_active();
        // Initialize timer with new interval.
        if (was_active)
            stop();
        _interval_ms = interval_ms;
        if (was_active)
            start();
    ;

    inline bool is_periodic(void) const  return _is_periodic; 
    inline void set_periodic(const bool is_periodic = true)  _is_periodic = is_periodic; 

private:
    // The timer worker for the interval thread.
    struct timer_worker 
        void operator()(const int interval_ms, const bool is_periodic, boost::shared_ptr<interval_signal> signal_expired)
        
            boost::posix_time::milliseconds duration(interval_ms);
            try 
                do 
                    boost::this_thread::sleep<boost::posix_time::milliseconds>(duration);
                    
                        boost::this_thread::disable_interruption di;
                        signal_expired->operator()();
                    
                 while (is_periodic);
             catch (const boost::thread_interrupted &) 
                // IntervalThread start(), stop() and reset() throws boost::this_thread::interrupt,
                // which is expected since this thread is interrupted. No action neccessary.
            
        ;
    ;

    std::atomic<int> _interval_ms;  // Interval, in ms
    std::atomic<bool> _is_active;   // Is the timed interval active?
    std::atomic<bool> _is_periodic; // Is the timer periodic?

    boost::mutex _timer_thread_guard;
    boost::thread _timer_thread;

    // The signal to call on interval expiration.
    boost::shared_ptr<interval_signal> _signal_expired;
;

【讨论】:

以上是关于我可以使用 Boost Signals2 和 Threads 在 C++ 中创建软件看门狗定时器线程吗?的主要内容,如果未能解决你的问题,请参考以下文章

使用 boost::signals2 和卸载 DLL 时访问冲突

我可以使用 Boost Signals2 和 Threads 在 C++ 中创建软件看门狗定时器线程吗?

boost--signal

使用 boost::signals2 的编译时间非常慢

如何比较 boost::signals2 中的插槽类型

在地图中存储 boost::signals2 信号?