使用多个线程的asio :: io_service优先级队列处理

时间:2018-08-23 17:35:26

标签: c++ multithreading boost-asio

我在多线程C ++代码中经常使用asio :: io_service。最近,由于在处理各种任务时没有优先级,我在代码中发现了一个瓶颈。我自然会遇到这种example提升的情况,以确保某些任务的优先级高于休息。但是此示例仅在单线程应用程序中有效。

通常,我的代码使用这种模式。

boost::asio::io_service ioService;
boost::thread_group threadPool;
boost::asio::io_service::work work(ioService);

int noOfCores = boost::thread::hardware_concurrency();
for (int i = 0 ; i < noOfCores ; i ++)
{
    threadPool.create_thread(boost::bind(&boost::asio::io_service::run, &ioService));
}
threadPool.join_all();

我从其他各种线程中处理了很多ioService.post(),所有这些处理程序都具有相同的优先级。

现在,如果我要使用boost示例中的 handler_priority_queue ,首先必须为add()和execute_all()函数添加一些互斥保护。

boost::mutex _mtx;
void add(int priority, boost::function<void()> function)
{
    boost::lock_guard<boost::mutex> lock(_mtx);
    handlers_.push(queued_handler(priority, function));
}

void execute_all()
{
    while (!handlers_.empty())
    {
        boost::unique_lock<boost::mutex> lock(_mtx);
        queued_handler handler = handlers_.top();
        handlers_.pop();
        lock.unlock();
        handler.execute();
    }
}

但是,我不确定用什么替换当前代码中的以下行。

    threadPool.create_thread(boost::bind(&boost::asio::io_service::run, &ioService));

我显然需要以某种方式将io_service :: run替换为handler_priority_queue :: execute_all()。但是如何?最好的方法是什么?

我可以做到...

    threadPool.create_thread(boost::bind(&handler_priority_queue::execute_all,
&pri_queue));

但是execute_all()立即出现。我认为execute_all()需要一些重新设计。这个怎么样?可以,但是我不确定会遇到的陷阱。

void execute_all()
{
    while (ioService.run_one())
    {
        while (ioService.poll_one());
        while (true)
        {
            queued_handler handler;
            {
                boost::lock_guard<boost::mutex> lock(_mtx);
                if (handlers_.empty())
                {
                    break;
                }
                else
                {
                    handler = handlers_.top();
                    handlers_.pop();
                }
            }
            handler.execute();
        }
    }
}

1 个答案:

答案 0 :(得分:1)

Asio没有提供这种可能性。该示例仅限于单个线程,因为它不会修改Asio的调度程序。调度程序以FIFO方式在线程之间分配任务,我不知道有任何修改方法。只要启动异步操作(例如io_service::post时)都无法指定优先级,调度程序就不会知道任务优先级,因此无法使用它。

当然,您可以在每个线程中使用priority_queue,但是在这种情况下,您的优先级将产生有限的“线程本地”效果:仅将调度到同一线程的任务根据其优先级执行。考虑示例(伪代码):

io_service.post(task(priority_1));
io_service.post(task(priority_2));
io_service.post(task(priority_3));

thread_1(io_service.run());
thread_2(io_service.run());

让我们假设任务1和3由thread_1承担,任务2由thread_2承担。因此,如果像链接示例中那样使用优先级队列,thread_1将执行任务3,然后执行任务1。但是thread_2对这些任务一无所知,将立即执行任务2,可能在任务3之前执行。

您的选择是实施自己的调度程序(复杂度取决于您的要求,但总的来说可能很棘手)或找到第三方解决方案。例如。我会检查Intel TBB Priority

编辑:尝试阐述“自己的调度程序”案例:

您需要一个非常好的多个生产者/多个消费者并发队列,以获取更简单的版本,线程池和从队列中拉出的线程。从优先级的角度来看,这将是一个相当公平的解决方案:所有较高优先级的任务(几乎总是)将在较低优先级的任务之前开始执行。如果性能比公平更重要,则可以改进此解决方案。但这值得您再提一个问题,以及有关您的特定案例的许多细节。