boost :: asio - никакой обработчик не вызывается при использовании post (), работает, когда функция вызывается напрямую (io_context работает) - PullRequest
1 голос
/ 29 мая 2019

Я пытаюсь периодически инициировать запрос от приложения к серверу с помощью таймера.Вызываемая функция ожидает завершения, используя boost :: обещание (в случае, если она вызывается вручную и требуется отображение состояния успеха).При запуске я вызываю функцию напрямую, и она завершается без проблем.Затем таймер периодически вызывает его снова, но когда инициируется через deadline_timer, обещание никогда не выполняется.

При вызове через .post () соединение с сервером открывается, но на стороне клиента обработчик handle_connect никогда не срабатывает.Для io_context назначена работа.

Я уже пытался переместить обещание в класс ServiceRequest вместо передачи ссылки, а также реализовать его в качестве члена класса, чтобы исключить проблемы времени жизни.

Iуменьшил всю проблему до минимального примера ошибочного кода:

(Демонстрации на Coliru: Работает (через прямой вызов) , Не работает (через почту) )

class ServiceRequest : public boost::enable_shared_from_this<ServiceRequest>
{
    public:

        ServiceRequest(boost::asio::io_service& io_service, Client& client, boost::promise<bool>& promise)
          : io_service_(io_service),
            socket_(io_service_),
            client_(client),
            promise_(promise)
        {}

        ~ServiceRequest()
        {}

        void Start()
        {
            socket_.async_connect(
                boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 3005),
                boost::bind(&ServiceRequest::handle_connect,
                            shared_from_this(),
                            boost::asio::placeholders::error
                )
            );
        }

    private: 

        void handle_connect(const boost::system::error_code& ec)
        {
            if(!ec)
            {
                promise_.set_value(true);

                boost::asio::async_write(socket_,
                                         boost::asio::buffer("Test"),                                                
                                         boost::bind(&ServiceRequest::close_socket, 
                                                     shared_from_this())
                                        );              

            }
            else
            {           
                promise_.set_value(false);
            }           
        }

        void close_socket()
        {
            socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);              
            socket_.close();                                                        
        }

        boost::asio::io_service&        io_service_;
        boost::asio::ip::tcp::socket    socket_;
        Client&                         client_;
        boost::promise<bool>&           promise_;       

};

class RequestHandler
{

    public:

        RequestHandler(boost::asio::io_service& io_service, Client& client)
          : io_service_(io_service),
            client_(client)
        {}

        ~RequestHandler()
        {}

        bool RequestService()
        {

            boost::promise<bool> promise;
            boost::shared_ptr<ServiceRequest> service_request = boost::make_shared<ServiceRequest>(io_service_, client_, promise);
            service_request->Start();

            bool result = promise.get_future().get();

            return result;          
        }

    private:

        boost::asio::io_service&    io_service_;
        Client&                     client_;

};

class Client {

    public:

        Client()
          : io_service_(),
            work_(io_service_),
            thread_group_(),
            timer_(io_service_),
            request_handler_(io_service_, *this)
        {
            thread_group_.create_thread(boost::bind(&boost::asio::io_service::run, &io_service_));
        }

        ~Client()
        {
            io_service_.stop();
            thread_group_.join_all();
        }

        void RequestService()
        {
            io_service_.post(boost::bind(&RequestHandler::RequestService, &request_handler_));  // <<--- deadlocks at promise.get_future().get()
            request_handler_.RequestService(); // <<--- works
            timer_.expires_from_now(boost::posix_time::seconds(10));
            timer_.async_wait(boost::bind(&Client::RequestService, this)); // <<--- deadlocks at promise.get_future().get()
        }

    private:

        boost::asio::io_service         io_service_;
        boost::asio::io_service::work   work_;
        boost::thread_group             thread_group_;
        boost::asio::deadline_timer     timer_;
        RequestHandler                  request_handler_;

};

int main()
{
    Client client;
    client.RequestService();    
    return 0;
}

При непосредственном вызове request_handler_.RequestService () все работает как положено.Отслеживание обработчика boost :: asio показывает, как и ожидалось:

@asio|1559149650.446538|0*1|socket@00000000007b9d40.async_connect
@asio|1559149650.456538|>1|ec=system:0
@asio|1559149650.456538|1*2|socket@00000000007b9d40.async_send
@asio|1559149650.456538|<1|
@asio|1559149650.456538|>2|ec=system:0,bytes_transferred=5
@asio|1559149650.456538|2|socket@00000000007b9d40.close
@asio|1559149650.456538|<2|

При использовании .post () или таймера крайнего срока для вызова RequestService () средство отслеживания обработчика показывает:

@asio|1559149477.071693|0*1|io_context@000000000022fd90.post
@asio|1559149477.071693|>1|
@asio|1559149477.071693|1*2|socket@00000000007b9e10.async_connect

Таким образом, соединение установлено, но обработчик не запущен, и, следовательно, не вызывается файл promise.set_value (bool), и все это блокируется.

Что я здесь не так делаю?

1 Ответ

1 голос
/ 29 мая 2019

У вас есть только один поток, который вызывает io_service::run.

post() выполняет функцию, которую вы ей даете из io_service в одном из потоков io_service. Функция, которую вы пытаетесь запустить в основном цикле io_service (RequestHandler::RequestService), является блокирующей функцией, которая ожидает выполнения обещания той работой, которая должна выполняться в потоке io_service. Это никогда не завершится, потому что вы заблокировали поток io_service.

Это одна из основных ошибок, которую вам следует избегать при использовании ASIO или любой асинхронной среды. Никогда не блокируйте в потоке, который обрабатывает ваши события, потому что вы можете ввести тонкие (или не очень тонкие) взаимоблокировки, подобные этой.

...