Puis-je créer un thread de chien de garde logiciel en C ++ en utilisant Boost Signals2 et Threads?

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

Question

J'utilise actuellement la fonction Foo de la bibliothèque de quelqu'un d'autre dans une application à un seul thread. La plupart du temps, j'appelle Foo et c'est très rapide. Parfois, j'appelle Foo et cela prend une éternité. Je ne suis pas un homme patient, si Foo doit durer une éternité, je veux arrêter l'exécution de Foo et ne pas l'appeler avec ces arguments.

Quel est le meilleur moyen d'appeler Foo de manière contrôlée (mon environnement actuel est POSIX / C ++) de sorte que je puisse arrêter l'exécution après un certain nombre de secondes. Je pense que la bonne chose à faire ici est de créer un deuxième fil pour appeler Foo, tandis que dans mon fil principal, je crée une fonction de minuterie qui signalera éventuellement le deuxième fil s'il manque de temps.

Existe-t-il un autre modèle (et une solution) plus apte? Sinon, est-ce que la bibliothèque et les threads de Boost Signals2 feraient l'affaire?

Était-ce utile?

La solution

Vous pouvez appeler Foo sur un deuxième thread avec un délai d'attente. Par exemple:

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

Autres conseils

Vous pouvez utiliser la classe suivante:

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

Un exemple d'utilisation:

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

Vous pouvez également définir une alarme juste avant d'appeler cette fonction et intercepter SIGALRM.

Vlad, excellent post! Votre code compilé et fonctionne à merveille. J'ai implémenté une horloge de surveillance logicielle avec elle. J'ai fait quelques modifications:

  • Pour éviter la dégradation du pointeur, stockez le signal dans boost :: shared_ptr et transmettez-le au threadwork au lieu d'un pointeur faible vers la classe timer. Ceci élimine la nécessité pour le travailleur du fil d'être une structure amie et garantit que le signal est en mémoire.
  • Ajoutez le paramètre _is_periodic pour permettre à l'appelant de choisir si le thread de travail est périodique ou s'il se termine après l'expiration.
  • Stockez _is_active, _interval et _is_periodic dans boost :: atomic pour autoriser les accès thread-safe.
  • Réduisez la portée du verrouillage mutex.
  • Ajouter la méthode reset () à "Kick" la minuterie, l'empêchant d'émettre le signal d'expiration.

Avec ces modifications appliquées:

#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;
};
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top