ASIO:它是否为 运行 不同线程中的完成处理程序定义了行为?

ASIO: Is it defined behavior to run a completion handler in a different thread?

在以下示例中,计时器与 io_context 执行程序相关联。但是随后,处理程序被告知在线程池中执行。原因是,因为处理程序实际上执行阻塞代码,而我不想阻塞 io_context.

run 函数

但是文档states

Handlers are invoked only by a thread that is currently calling any overload of run(), run_one(), run_for(), run_until(), poll() or poll_one() for the io_context.

如代码所示,处理程序由 thread_pool 在 run 之外调用。这种行为定义明确吗?它也适用于套接字吗?

#include <boost/asio.hpp>
#include <iostream>

int main() {
    using namespace boost::asio;
    thread_pool tp(4);

    io_context ioc;
    deadline_timer dt(ioc, boost::posix_time::milliseconds(500));
    dt.async_wait(bind_executor(tp, [&](boost::system::error_code){
        std::cout << "running in tp: " << tp.get_executor().running_in_this_thread() << std::endl;
        std::cout << "running in ioc: " << ioc.get_executor().running_in_this_thread() << std::endl;
        exit(0);
    }));

    auto wg = make_work_guard(ioc);
    ioc.run(); 
}
running in tp: 1
running in ioc: 0

Godbolt link.

要事第一。

您可以在任何地方 运行 处理程序。它是否会导致 UB 取决于您在处理程序中执行的操作。

我会将您的问题解释为询问“观察到的行为是否与记录的 requirements/guarantees 处理程序调用相矛盾?”

这是否与文档相矛盾?

没有。

您有两个不同的执行上下文。其中之一是 io_context (您链接到其文档),另一个是 thread_pool.

您已要求在 tp 上调用处理程序(通过将处理程序绑定到其执行程序)。

因此,deadline_timer 在构造时绑定到的默认执行程序被处理程序的 关联执行程序 否决。

您在查看不适用此案例的 io_context 文档时感到困惑:deadline-timer 的完成永远不会发布到 io_context。根据您的具体要求,它已发布到 thread_pool 的上下文中。

正如预期的那样,在执行上下文 tp.

的线程 运行 上调用处理程序

化繁为简

这是一个直接使用 post 的简化示例,而不是通过任意异步启动函数来执行此操作。

此外,它还会执行目标执行器和关联执行器的所有组合(如果有的话)。

Live On Coliru

#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
namespace asio = boost::asio;

int main() {
    std::cout << std::boolalpha << std::left;

    asio::io_context ioc;
    asio::thread_pool tp(4);

    auto xioc = ioc.get_executor();
    auto xtp  = tp.get_executor();

    static std::mutex mx; // prevent jumbled output

    auto report = [=](char const* id, auto expected) {
        std::lock_guard lk(mx);

        std::cout << std::setw(11) << id << " running in tp/ioc? "
                  << xtp.running_in_this_thread() << '/'
                  << xioc.running_in_this_thread() << " "
                  << (expected.running_in_this_thread() ? "Ok" : "INCORRECT")
                  << std::endl;
    };

    asio::post(tp,  [=] { report("direct tp", xtp); });
    asio::post(ioc, [=] { report("direct ioc", xioc); });

    asio::post(ioc, bind_executor (tp,  [=] { report("bound tp.A", xtp); }));
    asio::post(tp,  bind_executor (tp,  [=] { report("bound tp.B", xtp); }));
    asio::post(     bind_executor (tp,  [=] { report("bound tp.C", xtp); }));

    asio::post(ioc, bind_executor (ioc, [=] { report("bound ioc.A", xioc); }));
    asio::post(tp,  bind_executor (ioc, [=] { report("bound ioc.B", xioc); }));
    asio::post(     bind_executor (ioc, [=] { report("bound ioc.C", xioc); }));

    // system_executor doesn't have .running_in_this_thread()
    // asio::post([=] { report("system", asio::system_executor{}); });

    ioc.run();
    tp.join();
}

打印例如

direct tp   running in tp/ioc? true/false Ok
direct ioc  running in tp/ioc? false/true Ok
bound tp.C  running in tp/ioc? true/false Ok
bound tp.B  running in tp/ioc? true/false Ok
bound tp.A  running in tp/ioc? true/false Ok
bound ioc.C running in tp/ioc? false/true Ok
bound ioc.B running in tp/ioc? false/true Ok
bound ioc.A running in tp/ioc? false/true Ok

注意

  • 绑定的执行者总是占上风
  • 有系统执行器的回退 (see its effect)

有关 post 的语义,请参阅 the docs or e.g.