2009-06-18 21 views
9

Tôi đang chạy hàm Foo từ thư viện của người khác trong một ứng dụng đơn luồng. Hầu hết thời gian, tôi gọi điện cho Foo và nó thực sự nhanh chóng, một vài lần, tôi gọi Foo và phải mất mãi mãi. Tôi không phải là một người đàn ông bệnh nhân, nếu Foo sẽ mãi mãi, tôi muốn ngừng thực hiện Foo và không gọi nó với những lý lẽ đó.Tôi có thể tạo một chuỗi bộ đếm thời gian của bộ giám sát phần mềm trong C++ bằng cách sử dụng Boost Signals2 và Threads?

Cách tốt nhất để gọi Foo theo cách được điều khiển (môi trường hiện tại của tôi là POSIX/C++) sao cho tôi có thể ngừng thực hiện sau một số giây nhất định. Tôi cảm thấy điều đúng đắn cần làm ở đây là tạo ra một chuỗi thứ hai để gọi Foo, trong khi trong chủ đề chính của tôi, tôi tạo ra một chức năng hẹn giờ mà cuối cùng sẽ báo hiệu luồng thứ hai nếu nó hết thời gian.

Có mô hình apt và giải pháp nào khác không? Nếu không, thư viện Signals2 và Chủ đề của Boost sẽ thực hiện thủ thuật?

Trả lời

10

Bạn có thể gọi Foo trên chuỗi thứ hai với thời gian chờ. Ví dụ:

#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; 
} 
+1

Chỉ cần làm rõ, 'timed_join' * sẽ không * chấm dứt 'Foo()' thực hiện chủ đề nếu thời gian chờ được đạt tới, như tôi nghĩ Aron đã yêu cầu cho. Thay vào đó, người gọi sẽ chỉ biết rằng chuỗi 'Foo()' vẫn đang chạy khi hết thời gian chờ. – pilcrow

2

Bạn cũng có thể đặt báo thức ngay trước khi gọi chức năng đó và bắt SIGALRM.

6

Bạn có thể sử dụng lớp sau:

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; 
}; 

Một ví dụ về việc sử dụng:

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); 
} 
1

Vlad, bưu tuyệt vời! Mã của bạn được biên dịch và hoạt động rất đẹp. Tôi đã thực hiện một bộ đếm thời gian watchdog phần mềm với nó. Tôi thực hiện một vài thay đổi:

  • Để ngăn ngừa sâu con trỏ, lưu trữ các tín hiệu trong tăng :: shared_ptr và chuyển thông tin này cho người lao động chủ đề thay vì một con trỏ yếu đến lớp hẹn giờ. Điều này giúp loại bỏ sự cần thiết cho các công nhân thread là một người bạn struct và đảm bảo tín hiệu trong bộ nhớ.
  • Thêm thông số _is_periodic để cho phép người gọi chọn xem chuỗi công việc có định kỳ hay không nếu nó chấm dứt sau khi hết hạn.
  • Lưu trữ _is_active, _interval và _is_periodic trong tăng :: nguyên tử để cho phép truy cập an toàn chỉ.
  • Thu hẹp phạm vi khóa mutex.
  • Thêm phương thức reset() để "kick" bộ hẹn giờ, ngăn không cho nó phát hành tín hiệu hết hạn.

Với những thay đổi này được áp dụng:

#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; 
};