2009-06-18 2 views
9

현재 단일 스레드 응용 프로그램에서 다른 사람의 라이브러리에서 Foo라는 함수를 실행 중입니다. 대부분의 시간에 Foo에 전화를 걸면 정말 빨라요. Foo에 전화를 걸면 영원히 걸립니다. 나는 참을성있는 사람이 아니다. 만약 Foo가 영원히 받아 들여지면 Foo의 실행을 멈추고 그 주장으로 부르지 않기를 원한다.Boost Signals2와 Threads를 사용하여 C++에서 소프트웨어 워치 독 타이머 스레드를 생성 할 수 있습니까?

Foo를 제어 된 방식으로 호출하는 가장 좋은 방법은 (현재 환경은 POSIX/C++ 임) 특정 시간 (초) 후에 실행을 중지 할 수 있습니다. Foo를 호출 할 두 번째 스레드를 만드는 것이 옳은 것처럼 느껴지지만, 내 메인 스레드에서는 시간이 만료되면 두 번째 스레드에 신호를 보내는 타이머 함수를 만듭니다.

다른 적합한 모델 (및 솔루션)이 있습니까? 그렇지 않다면, Boost의 Signals2 라이브러리와 Threads가 트릭을 할 것입니까?

답변

10

타임 아웃이있는 두 번째 스레드에서 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; 
} 
+1

그냥'timed_join' *이 *은'푸()가'생각대로 제한 시간에 도달하면 스레드의 실행이 아론이 물어 봤다 멈추지 않을 것입니다, 명확하게 에 대한. 대신, 호출자는 단지 타임 아웃에 도달했을 때'Foo()'쓰레드가 계속 실행 중임을 알 것이다. – pilcrow

2

해당 기능을 호출하기 전에 알람을 설정하고 SIGALRM을 잡을 수도 있습니다.

6

당신은 다음과 같은 클래스를 사용할 수 있습니다

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

블라드, 우수한 게시물을! 코드가 컴파일되고 아름답게 작동합니다. 나는 소프트웨어 워치 독 타이머를 구현했다.

  • 포인터 감쇠를 방지하려면 신호를 boost :: shared_ptr에 저장하고 이것을 타이머 클래스의 약한 포인터 대신 스레드 작업자에게 전달하십시오. 이렇게하면 스레드 작업자가 친구 구조체가 될 필요가 없으므로 신호가 메모리에 있음을 보장 할 수 있습니다.
  • _is_periodic 매개 변수를 추가하여 호출자가 작업자 스레드가 주기적인지 또는 만료 후에 종료되는지 여부를 선택할 수있게합니다.
  • 스레드 안전 액세스를 허용하기 위해 boost :: atomic에 _is_active, _interval 및 _is_periodic을 저장하십시오.
  • 뮤텍스 잠금 범위를 좁히십시오.
  • timer를 "실행"하여 만료 신호를 내지 않도록 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; 
}; 
관련 문제