Остановка запланированного задания с помощью SIGINT - PullRequest
0 голосов
/ 05 декабря 2018

Фон

Я пытаюсь остановить периодические задачи, когда пользователь прерывает процесс с помощью SIGINT.Мой планировщик периодических задач основан на этом ответе .

. Для этого я попытался передать указатель экземпляра PeriodicScheduler на мой InterruptHandler и вызвать ps->stop().

Планировщик периодических задачheader:

#ifndef __PERIODICSCHEDULER_H__
#define __PERIODICSCHEDULER_H__

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>

namespace APP{
    class PeriodicTask : boost::noncopyable {
        public:
            typedef std::function<void()> handler_fn;

            PeriodicTask(boost::asio::io_service& ioService
                , std::string const& name
                , int interval
                , handler_fn task);
            void execute(boost::system::error_code const& e);
            void start();

        private:
            void start_wait();

            boost::asio::io_service& ioService;
            boost::asio::deadline_timer timer;
            handler_fn task;
            std::string name;
            int interval;

    }; /* class PeriodicTask */

    class PeriodicScheduler : boost::noncopyable
    {
        public:
            template<typename T, typename... Args>
            std::unique_ptr<T> make_unique(Args&&... args) {
                return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
            }

            void run();
            void stop();
            void addTask(std::string const& name
                                            , PeriodicTask::handler_fn const& task
                                            , int interval);

        private:
            boost::asio::io_service io_service;
            std::vector<std::unique_ptr<PeriodicTask>> tasks;
    }; /* PeriodicScheduler */
} /* namespace Resto */

#endif /* __PERIODICSCHEDULER_H__ */

Источник планировщика периодических задач:

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>

#include "periodicScheduler.h"

APP::PeriodicTask::PeriodicTask(boost::asio::io_service& ioService
            , std::string const& name
            , int interval
            , handler_fn task)
            : ioService(ioService)
            , interval(interval)
            , task(task)
            , name(name)
            , timer(ioService){
            // Schedule start to be ran by the io_service
            ioService.post(boost::bind(&PeriodicTask::start, this));
        }

void APP::PeriodicTask::execute(boost::system::error_code const& e){
    if (e != boost::asio::error::operation_aborted) {

        task();

        timer.expires_at(timer.expires_at() + boost::posix_time::seconds(interval));
        start_wait();
    }
}

void APP::PeriodicTask::start(){

    // Uncomment if you want to call the handler on startup (i.e. at time 0)
    // task();

    timer.expires_from_now(boost::posix_time::seconds(interval));
    start_wait();
}

void APP::PeriodicTask::start_wait(){
    timer.async_wait(boost::bind(&PeriodicTask::execute
        , this
        , boost::asio::placeholders::error));
}



void APP::PeriodicScheduler::run(){
    io_service.run();
}

void APP::PeriodicScheduler::stop(){
    io_service.stop();
}

void APP::PeriodicScheduler::addTask(std::string const& name
    , PeriodicTask::handler_fn const& task
    , int interval){
    tasks.push_back(make_unique<PeriodicTask>(std::ref(io_service)
        , name, interval, task));
}

Следующее является InterruptHandler:

#include <csignal>
#include <condition_variable>
#include <mutex>
#include <iostream>
#include <boost/asio.hpp>

#include "periodicScheduler.h"

static std::condition_variable _condition;
static std::mutex _mutex;

namespace APP {
    class InterruptHandler {
    public:
        static void hookSIGINT() {
            signal(SIGINT, handleUserInterrupt);        
        }

        static void handleUserInterrupt(int signal){
            if (signal == SIGINT) {
                std::cout << "SIGINT trapped ..." << '\n';
                _condition.notify_one();
            }
        }

        static void waitForUserInterrupt(APP::PeriodicScheduler *ps) {
            std::unique_lock<std::mutex> lock { _mutex };
            _condition.wait(lock);
            ps->stop();
            std::cout << "user has signaled to interrup program..." << '\n';
            lock.unlock();
        }
    };
}

My main()

int main(int ac, const char * av[]) {

    InterruptHandler::hookSIGINT();

    APP::PeriodicScheduler ps;

    APP::WorkerClass wc;

    // WorkerClass::someTask and WorkerClass:someOtherTask are dummy functions only with sleep(5); inside them

    ps.addTask("someTask", boost::bind( &APP::WorkerClass::someTask, wc ), 60);
    ps.addTask("someOtherTask", boost::bind( &APP::WorkerClass::someOtherTask, wc ), 60);

    ps.run();

    InterruptHandler::waitForUserInterrupt(&ps);

    return 0;
}

Issue

После запуска моего приложения в терминале я нажал CTRL + C, чтобы вызвать прерывание.Я могу видеть SIGINT trapped ... в терминале, но приложение продолжает работать.

Если я закомментирую оператор ps.run();, при нажатии CTRL + CI можно увидеть SIGINT trapped ..., затем user has signaled to interrup program... и приложение завершится.

Вопросы

Правильно ли подходит мой подход?Как эффективно остановить запланированные задания и выйти из приложения?

Я что-то пропустил?

1 Ответ

0 голосов
/ 05 декабря 2018

Во что бы то ни стало, я бы предложил использовать signal_set https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/signal_set.html

Вот несколько примеров: https://stackoverflow.com/search?q=user%3A85371+signal_set

Самое приятное то, что это изолирует вас от какой-то конкретной платформыустраняет распространенные ошибки, связанные с написанием асинхронно-безопасных обработчиков.

...