هل يمكنني إنشاء مؤشر ترابط مؤقت لمراقبة البرامج في C++ باستخدام Boost Signals2 وThreads؟

StackOverflow https://stackoverflow.com/questions/1015494

سؤال

أقوم بتشغيل وظيفة Foo من مكتبة شخص آخر في تطبيق ذي ترابط واحد حاليًا.في أغلب الأحيان، أقوم بإجراء مكالمة مع Foo ويكون الأمر سريعًا للغاية، وفي بعض الأحيان، أقوم بإجراء مكالمة مع Foo ويستغرق الأمر وقتًا طويلاً.أنا لست رجلاً صبورًا، إذا كان Foo سيستغرق إلى الأبد، فأنا أريد إيقاف تنفيذ Foo وعدم استدعائه بهذه الحجج.

ما هي أفضل طريقة للاتصال بـ Foo بطريقة خاضعة للرقابة (بيئتي الحالية هي POSIX/C++) بحيث يمكنني إيقاف التنفيذ بعد عدد معين من الثواني.أشعر أن الشيء الصحيح الذي يجب فعله هنا هو إنشاء سلسلة رسائل ثانية للاتصال بـ Foo، بينما أقوم في صفحتي الرئيسية بإنشاء وظيفة مؤقت ستشير في النهاية إلى السلسلة الثانية في حالة نفاد الوقت.

هل هناك نموذج (وحل) آخر أكثر ملاءمة؟إذا لم يكن الأمر كذلك، فهل ستؤدي مكتبة Boost's Signals2 وThreads المهمة؟

هل كانت مفيدة؟

المحلول

ويمكنك الاتصال فو على موضوع الثاني مع مهلة. على سبيل المثال:

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

نصائح أخرى

ويمكنك استخدام فئة التالية:

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

ويمكنك أيضا ضبط المنبه حق قبل استدعاء هذه الوظيفة، وSIGALRM الصيد.

فلاد، وظيفة ممتازة!تم تجميع التعليمات البرمجية الخاصة بك وتعمل بشكل جميل.لقد قمت بتطبيق مؤقت مراقبة البرنامج معه.لقد قمت ببعض التعديلات:

  • لمنع تدهور المؤشر، قم بتخزين الإشارة في Boost::shared_ptr وتمريرها إلى عامل مؤشر الترابط بدلاً من المؤشر الضعيف إلى فئة المؤقت.وهذا يلغي الحاجة إلى أن يكون عامل الخيط صديقًا ويضمن وجود الإشارة في الذاكرة.
  • أضف المعلمة _is_periodic للسماح للمتصل بتحديد ما إذا كان مؤشر ترابط العامل دوريًا أم لا أو إذا كان ينتهي بعد انتهاء الصلاحية.
  • قم بتخزين _is_active و_interval و_is_periodic في Boost::atomic للسماح بالوصول الآمن لمؤشر الترابط.
  • تضييق نطاق قفل كائن المزامنة (mutex locking).
  • أضف طريقة إعادة التعيين () إلى "ركلة" المؤقت، مما يمنعه من إصدار إشارة انتهاء الصلاحية.

مع تطبيق هذه التغييرات:

#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;
};
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top