如何为io_context创建优先级调度程序?

问题描述 投票:0回答:1

我正在编写一个 ASIO 应用程序,它在某些事件发生后对延迟敏感。我希望每个协程都有一个与之相关的优先级。对延迟敏感的协程总是会先于其他协程执行。 (实际上优先级会多于两个,但基本思想是较高级别始终优先于较低级别。让较低级别挨饿是可以接受的。)

协程可能在不同优先级之间进行通信。例如,他们可能会

co_spawn
与自己不同级别的协程,或者他们可能会升高/降低不同级别的协程正在等待的信号量。

我怎样才能做到这一点? https://live.boost.org/doc/libs/1_83_0/doc/html/boost_asio/example/cpp14/executors/priority_scheduler.cpp有优先级调度程序的旧示例,但它适用于以下上下文不是

io_context
。我怀疑我无法运行协程来执行 IO 操作。

我的应用程序是单线程的,以防万一。至少现在是这样。

我目前的想法是:

  1. 不是单个
    io_context
    对象有多个:每个优先级对应一个对象。
  2. 不要运行单个
    io_context::run()
    ,而是使用我自己的事件循环来运行
    io_contexts[high_priority].poll_one()
    ,并且仅当返回 0 时才运行
    io_contexts[lower_priority].poll_one()

文档很少,但我强烈地感觉到这不是 ASIO 设计者的初衷。我担心我可能会在不同 io_contexts 上运行的协程之间的性能或通信方面遇到麻烦。

我强烈意识到应该有一个具有多个执行器的 io_context,类似于上面链接的示例。

c++ c++20 boost-asio c++-coroutine
1个回答
0
投票

首先,您提供的带有单独 io_contexts 的草图看起来很聪明 - 从某种意义上说它应该可以工作。我怀疑你是对的,这样做会“选择退出”

io_context
及其执行者内部的许多优化空间。

我想指出的是,我认为

priority_scheduler
的例子并不古老 - 相反。但我承认它没有与 Asio 的任何执行上下文(有自己的调度程序)集成,并且查看它是否可以集成是很重要的。我自己目前也不确定。我怀疑该示例位于“c++14 执行器”部分中,因为它旨在作为网络工作组的(标准)提案的一部分,并且非常注重细节。从这个意义上说,它是一个强有力的例子,展示了如何独立应用正交 Asio 模型和概念。


可以提供的是另一个可能具有启发性的示例:调用:优先处理程序,位于 c++03 示例下。

它也反转了调度程序的一些控制,即使用自定义执行器将处理程序推送到优先级队列,然后执行该队列中的处理程序:

boost::asio::io_context io_context;

handler_priority_queue pri_queue;

// Post a completion handler to be run immediately.
boost::asio::post(io_context, pri_queue.wrap(0, low_priority_handler));

// Start an asynchronous accept that will complete immediately.
tcp::endpoint endpoint(boost::asio::ip::address_v4::loopback(), 0);
tcp::acceptor acceptor(io_context, endpoint);
tcp::socket   server_socket(io_context);
acceptor.async_accept(server_socket, pri_queue.wrap(100, high_priority_handler));
tcp::socket client_socket(io_context);
client_socket.connect(acceptor.local_endpoint());

// Set a deadline timer to expire immediately.
boost::asio::steady_timer timer(io_context);
timer.expires_at(boost::asio::steady_timer::time_point::min());
timer.async_wait(pri_queue.wrap(42, middle_priority_handler));

while (io_context.run_one()) {
    // The custom invocation hook adds the handlers to the priority queue
    // rather than executing them from within the poll_one() call.
    while (io_context.poll_one())
        ;

    pri_queue.execute_all();
}

我觉得可以从 c++14 示例中复制一些优化(特别是类型擦除而不是

std::function
² 以及移动感知处理程序支持的一些实例)。

(1 松散命名是因为我远离 ISO 委员会和工作组中的政治和组织结构 - 这不是我试图遵循的东西)

(² 请注意,c++14 示例中使用的模式对于调用处理程序之前释放资源的顺序非常重要。这一微妙点对于保留 Asio 的分配顺序保证非常重要:“As前面提到,在调用完成处理程序之前必须释放所有资源。这使得内存能够被回收以用于代理内的后续异步操作。这使得具有长寿命异步代理的应用程序没有热路径内存分配,即使用户代码不知道关联的分配器”

© www.soinside.com 2019 - 2024. All rights reserved.