asio :: io_service сразу заканчивается работой - PullRequest
2 голосов
/ 25 апреля 2020

Я пытаюсь изучить io_service и работать с общими указателями. Я хочу, чтобы код работал бесконечно, пока я не вызову метод stop или что-то подобное. К сожалению, после просмотра вывода workHandler на экране программа закрывается. Кто-нибудь может объяснить, почему это произошло?

#include <boost/asio.hpp>
#include <iostream>
#include <atomic>
#include <memory>
#include <thread>
#include <vector>

class Service : public std::enable_shared_from_this<Service> {
    std::shared_ptr<boost::asio::io_service> _service;
    std::shared_ptr<boost::asio::io_service::work> _work;
    std::vector<std::thread> _threads;
    std::atomic<bool> _started{false};

public:
    Service() 
    : _service(std::make_shared<boost::asio::io_service>()),
    _work(std::make_shared<boost::asio::io_service::work>(*_service))
    {}

    void start() { 
        auto self(this->shared_from_this());
        auto startHandler = [this, self]() {
            std::cout << "StartHandler\n";
            while(!_started) _service->run();
        };

        _threads.emplace_back(std::thread(startHandler));
    }

    std::shared_ptr<boost::asio::io_service>& get() { return _service; }
};

class Worker : public std::enable_shared_from_this<Worker> {
    std::shared_ptr<Service> _service;
    std::shared_ptr<boost::asio::io_service> _io_service;

public:
    Worker(const std::shared_ptr<Service>& service)
    : _service(service),
    _io_service(_service->get())
    {}

    void work() {
        auto self(this->shared_from_this());
        auto workHandler = [this, self]() {
            std::cout << "WorkHandler\n";
        };

        _io_service->post(workHandler);
    }
};

int main() {
    auto ser = std::make_shared<Service>();
    ser->start();
    auto worker = std::make_shared<Worker>(ser);
    worker->work();
}

1 Ответ

2 голосов
/ 26 апреля 2020

Вы столкнулись с неопределенным поведением.

Ваши обработчики фиксируют общие указатели на объекты Service / Work. Но ничто не останавливает main от выхода, который запустит обработчики выхода и разрушит глобальную библиотечную инфраструктуру. Это не то, что вам нужно.

Проблемы вызваны чрезмерным использованием общих указателей. Общие указатели хороши ТОЛЬКО для совместного владения. В большинстве вашего кода нет общей собственности (main владеет Сервисом!). Упрощение:

Live On Coliru

#include <boost/asio.hpp>
#include <boost/optional.hpp>
#include <iostream>
#include <memory>
#include <thread>
#include <list>

namespace ba = boost::asio;

class Service {
    ba::io_service _service;
    boost::optional<ba::io_service::work> _work {_service};
    std::list<std::thread> _threads;

public:
    ~Service() {
        _work.reset(); // allow service to complete
        for (auto& thread : _threads)
            if (thread.joinable())
                thread.join();
    }

    void start() { 
        _threads.emplace_back([this] {
            _service.run();
        });
    }

    ba::io_service& get() { return _service; } 
};

class Worker : public std::enable_shared_from_this<Worker> {
    ba::io_service& _io;

public:
    Worker(Service& service) : _io(service.get()) {}

    void work() {
        auto self(shared_from_this());
        auto workHandler = [self]() {
            std::cout << "WorkHandler " << std::endl;
        };

        _io.post(workHandler);
    }
};

int main() {
    Service ser;
    ser.start();

    std::make_shared<Worker>(ser)->work();
}

Печать

WorkHandler

Но самое главное: не вызывает UB и выходит чисто через присоединение потоков.

...