【问题标题】:boost::asio no handler called when using post(), works when function called directly (io_context has work)boost::asio 使用 post() 时不调用处理程序,直接调用函数时有效(io_context 有效)
【发布时间】:2019-10-15 09:55:44
【问题描述】:

我正在尝试使用计时器定期触发从应用程序到服务器的请求。被调用函数通过利用 boost::promise 等待完成(如果手动调用它并且需要显示成功状态)。启动时我直接调用该函数,它完成没有问题。然后一个计时器会定期再次调用它,但是当通过 deadline_timer 启动时,承诺永远不会实现。

当通过 .post() 调用时,会打开与服务器的连接,但在客户端永远不会触发 handle_connect 处理程序。 io_context 已分配工作。

我已经尝试将承诺移至 ServiceRequest 类,而不是传递引用并将其作为类成员实现以排除生命周期问题。

我已将整个问题简化为失败代码的最小示例:

(Coliru 上的演示:Working (via direct call)Failing (via post)

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) 并且整个事情都锁定了。

我在这里做错了什么?

【问题讨论】:

    标签: c++ boost-asio future boost-thread


    【解决方案1】:

    你只有一个线程在调用io_service::run

    post() 在 io_service 的线程之一上执行您从 io_service 中赋予它的函数。您尝试在 io_service 的主循环 (RequestHandler::RequestService) 中运行的函数是一个阻塞函数,它正在等待应该在 io_service 线程上执行的工作来完成承诺。这永远不会完成,因为您已经阻止了 io_service 的线程。

    这是您在使用 ASIO 或任何异步框架时需要避免的主要错误之一。永远不要阻塞正在处理您的事件的线程,因为您可以像这样引入微妙(或不那么微妙)的死锁。

    【讨论】:

    • 感谢您的回答。现在我觉得有点愚蠢,因为我应该知道这一点。我想没有看到所有树木的森林,因为我试图避免多线程(复杂的原因,简短的解释:现有的代码库,目前正在重构,不是线程安全的,目标是到达那里)我现在已经把所有的调度/timers 在第二个线程中,操作结果通过回调函数发送给调用者。
    • 呵呵,没问题。我发现使用回调是处理异步结果的一种很好的最小公分母方法。它为您提供了链接另一个异步请求的选项,或者只是将结果填充到其他线程正在等待的承诺中。
    猜你喜欢
    • 1970-01-01
    • 2016-12-22
    • 1970-01-01
    • 2012-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多