当I/O对象已经有Executor时,为什么需要`net::Dispatch`?

2022-04-09 00:00:00 c++ boost-asio boost-beast

我正在从本例中学习Boost.Beast&;Boost.Asiolibs/beast/example/http/server/async-ssl/http_server_async_ssl.cpp - 1.77.0。

据我所知,发生在I/O对象上的所有I/O操作都发生在该对象的I/O执行上下文中。异步操作将与I/O上下文的run位于同一线程中,因为它们都由I/O上下文的run(间接)调用。

在本例中(请参见上面的链接),当连接建立时,接受者为新连接分配一条专用链:

    do_accept()
    {
        // The new connection gets its own strand
        acceptor_.async_accept(
            net::make_strand(ioc_),
            beast::bind_front_handler(
                &listener::on_accept,
                shared_from_this()));
    }

这是否意味着在新连接上发生的所有I/O操作都发生在链上?如果是,为什么该示例在要调用async_read时使用net::dispatch再次指定链?

    // Start the asynchronous operation
    void
    run()
    {
        // We need to be executing within a strand to perform async operations
        // on the I/O objects in this session. Although not strictly necessary
        // for single-threaded contexts, this example code is written to be
        // thread-safe by default.
        net::dispatch(
            stream_.get_executor(),
            beast::bind_front_handler(
                &session::on_run,
                shared_from_this()));
    }
如果我们直接调用async_read而不经过net::dispatch,有什么区别?谢谢。:)


解决方案

这是否意味着在新连接上发生的所有I/O操作都发生在链上?

这意味着接受的套接字将获得引用该链的Executor的副本。这确实意味着从该服务对象启动的所有异步操作在默认情况下(!)将在该链上调用它们的完成处理程序。但是,这不适用于任何其他操作,如启动函数(例如s.async_read_some)本身。

因此,为了确保所有操作都发生在链上,您必须确保所有启动都发生在同一链上。在许多情况下,这将是自动的,因为许多启动发生在前一个操作的完成中-因此已经在链上-但不是第一个操作,如您所见。

(可以说,在新链的开始处,因此当刚刚创建IO对象并且该链是该IO对象的私有时,可以从任何线程安全地进行第一次发起,逻辑上成为链中的一部分。你可以把它看作是链本身的分叉点(诞生)。这是因为没有正在运行的操作,也没有其他线程可能持有对它的引用。)

相关文章