保持两个交叉通信asio io_service对象忙

时间:2013-03-06 22:45:44

标签: c++ boost boost-asio

我正在使用boost:asio和多个io_services来保持不同形式的阻塞I / O分离。例如。我有一个io_service用于阻塞文件I / O,另一个用于长时间运行的CPU绑定任务(这可以扩展到第三个用于阻止网络I / O等)一般来说我想确保一种形式的阻止I / O不会使其他人挨饿。

我遇到的问题是,因为在一个io_service中运行的任务可以将事件发布到其他io_service(例如,CPU绑定的任务可能需要启动文件I / O操作,或者完成的文件I / O操作可能会调用一个CPU绑定的回调),我不知道如何保持两个io_services运行,直到它们都没有事件。

通常使用单个I / O服务,您可以执行以下操作:

 shared_ptr<asio::io_service> io_service (new asio::io_service);
 shared_ptr<asio::io_service::work> work (
   new asio::io_service::work(*io_service));

 // Create worker thread(s) that call io_service->run()

 io_service->post(/* some event */);

 work.reset();

 // Join worker thread(s)

但是,如果我只是为两个io_services执行此操作,那么我没有发布初始事件的那个会立即完成。即使我将初始事件发布到两者,如果io_service B上的初始事件在io_service A上的任务之前完成,则向B发布新事件,io_service B将提前完成。

如何在io_service A仍在处理事件时保持io_service B运行(因为服务A中的一个排队事件可能会向B发布新事件),反之亦然,同时仍然确保两个io_services都退出运行()方法,如果它们同时出现在两个事件之外?

2 个答案:

答案 0 :(得分:3)

找出一种方法来做到这一点,所以如果其他人在搜索中发现了这个问题,请将其记录下来以备记录:

  • 创建每个N个交叉通信的io_services,为每个io_services创建一个工作对象,然后启动它们的工作线程。

  • 创建一个“master”io_service对象,该对象不会运行任何工作线程。

  • 不允许将事件直接发布到服务。相反,为io_services创建访问器函数:

    1. 在主线程上创建工作对象。
    2. 将回调包装在运行真实回调的函数中,然后删除工作。
    3. 发布此包装回调。
  • 在主要的执行流程中,一旦所有N io_services都已启动并且您已将工作发布到至少其中一个,请在主io_service上调用run()。

  • 当主io_service的run()方法返回时,删除N交叉通信io_services上的所有初始工作,并加入所有工作线程。

让主io_service的线程自己在其他每个io_services上工作,确保它们不会在master io_service失去工作之前终止。让每个其他io_services在每个发布的回调的主io_service上工作,确保master io_service在其他每个io_services不再有任何已发布的回调留待处理之前不会失去工作。

一个例子(可以在类中封装):

shared_ptr<boost::asio::io_service> master_io_service;

void RunWorker(boost::shared_ptr<boost::asio::io_service> io_service) {
  io_service->run();
}

void RunCallbackAndDeleteWork(boost::function<void()> callback,
                              boost::asio::io_service::work* work) {
  callback();
  delete work;
}

// All new posted callbacks must come through here, rather than being posted
// directly to the io_service object.
void PostToService(boost::shared_ptr<boost::asio::io_service> io_service,
                   boost::function<void()> callback) {
  io_service->post(boost::bind(
      &RunCallbackAndDeleteWork, callback,
      new boost::asio::io_service::work(*master_io_service)));
}

int main() {
  vector<boost::shared_ptr<boost::asio::io_service> > io_services;
  vector<boost::shared_ptr<boost::asio::io_service::work> > initial_work;
  boost::thread_pool worker_threads;

  master_io_service.reset(new boost::asio::io_service);

  const int kNumServices = X;
  const int kNumWorkersPerService = Y;
  for (int i = 0; i < kNumServices; ++i) {
    shared_ptr<boost::asio::io_service> io_service(new boost::asio::io_service);
    io_services.push_back(io_service);
    initial_work.push_back(new boost::asio::io_service::work(*io_service));

    for (int j = 0; j < kNumWorkersPerService; ++j) {
      worker_threads.create_thread(boost::bind(&RunWorker, io_service));
    }
  }

  // Use PostToService to start initial task(s) on at least one of the services

  master_io_service->run();

  // At this point, there is no real work left in the services, only the work
  // objects in the initial_work vector.
  initial_work.clear();
  worker_threads.join_all();
  return 0;
}

答案 1 :(得分:2)

HTTP server example 2做了类似的事情,你可能觉得有用。它使用io_service池的概念,为vector保留shared_ptr<boost::asio::io_service>shared_ptr<boost::asio::io_service::work>io_service io_service。它使用线程池来运行每个服务。

该示例使用循环调度将工作分配给I / O服务,我认为这不适用于您的情况,因为您有io_service A和{{1}的特定任务B。