当 I/O 对象已经有一个执行器时,为什么还需要 net::dispatch?

Why is `net::dispatch` needed when the I/O object already has an executor?

我正在从这个例子中学习 Boost.Beast & Boost.Asio libs/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 调用(间接)。

在这个例子中(请见上文link),当连接建立时,接受者为新连接分配一个专用链:

    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()));
    }

不经过net::dispatch直接调用async_read有什么区别?谢谢。 :)

Does it imply that all I/O operations that happen on the new connections happen in the strand?

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

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

(可以说,在一个新链的开始,所以当一个 IO 对象刚刚被创建并且该链是该 IO 对象私有的时,第一次启动可以安全地从任何线程,逻辑上成为 strand 的一部分。您可以将其视为 strand 本身的分叉点(“诞生”)。那是因为没有任何操作可以进行中,并且没有其他线程可能持有对它的引用。)