以嵌套或递归方式(即在处理程序内)调用asio io_service poll()或poll_one()是否有效?

时间:2015-02-21 22:18:58

标签: c++ boost-asio

以嵌套或递归方式(即从处理程序内)调用asio :: io_service :: poll()或poll_one()是否有效?

一个真正基本的测试似乎暗示这是有效的(我只在一个平台上完成了测试)但我想确保从处理程序中再次调用poll()被认为是有效的行为。

我无法在asio文档中找到任何相关信息,所以我希望那些对asio内部工作有更多经验的人可以通过解释或参考来验证这一点。 / p>

基础测试:

struct NestedHandler
{
    NestedHandler(std::string name, asio::io_service * service) :
        name(name),
        service(service)
    {
        // empty
    }

    void operator()()
    {
        std::cout << " { ";
        std::cout << name;

        std::cout << " ...calling poll again... ";
        service->poll();
        std::cout << " } ";

    }

    std::string name;
    asio::io_service * service;
};

struct DefaultHandler
{
    DefaultHandler(std::string name) :
        name(name)
    {
        // empty
    }

    void operator()()
    {
        std::cout << " { ";
        std::cout << name;
        std::cout << " } ";
    }

    std::string name;
};

int main()
{
    asio::io_service service;
    service.post(NestedHandler("N",&service));
    service.post(DefaultHandler("A"));
    service.post(DefaultHandler("B"));
    service.post(DefaultHandler("C"));
    service.post(DefaultHandler("D"));

    std::cout << "asio poll" << std::endl;
    service.poll();

    return 0;
}

// Output:
asio poll
 { N ...calling poll again...  { A }  { B }  { C }  { D }  } 

2 个答案:

答案 0 :(得分:5)

这是有效的。

对于处理io_service的函数系列,run()是唯一有限制的函数:

  

不得从当前正在调用同一run()run()run_one()poll()之一的主题调用poll_one()函数{1}}对象。

但是,我倾向于认为文档也应该包含io_service的相同注释,因为嵌套调用可能导致它无限期地阻塞以下任何一种情况 [1]

  • run_one()中唯一的工作是当前正在执行的处理程序
  • 对于非I / O完成端口实现,唯一的工作是从当前处理程序中发布的,而io_service的并发提示为io_service

对于Windows I / O完成端口,在使用GetQueuedCompletionStatus()1提供服务的所有线程中执行解复用。在高级别,调用io_service的线程就像它们是线程池的一部分一样,允许操作系统将工作分配给每个线程。由于没有单个线程负责将操作解复用到其他线程,因此对GetQueuedCompletionStatus()poll()的嵌套调用不会影响其他线程的操作调度。 documentation州:

  

使用I / O完成端口进行多路分解是在所有线程中执行的,这些线程呼叫poll_one()io_service::run()io_service::run_one()io_service::poll()


对于所有其他解复用机制系统,服务io_service::poll_one()的单个线程用于解复用I / O操作。确切的解复用机制可以在Platform-Specific Implementation Notes

中找到
  

使用[io_service/dev/pollepollkqueue]进行多路分解是在一个调用{{1}的线程中执行的},selectio_service::run()io_service::run_one()

解复用机制的实现稍有不同,但处于高级别:

  • io_service::poll()有一个主队列,线程使用该队列执行准备运行的操作
  • 每次调用处理io_service::poll_one()都会在堆栈上创建一个私有队列,用于以无锁方式管理操作
  • 最终会发生与主队列的同步,其中获取锁并将私有队列操作复制到主队列中,通知其他线程,并允许它们从主队列中消耗。

io_serviceconstructed时,可能会提供并发提示,表明实现应允许并发运行的线程数。当非I / O完成端口实现提供io_service的并发提示时,它们被优化为尽可能多地使用专用队列并推迟与主队列的同步。例如,当通过io_service发布处理程序时:

  • 如果从处理程序外部调用,则1保证线程安全,以便在排队处理程序之前锁定主队列。
  • 如果从处理程序中调用,则已发布的处理程序将排入专用队列,从而延迟与主队列的延迟同步,直到需要为止。

当调用嵌套的post()io_service时,必须将私有队列复制到主队列中,因为要执行的操作将从主队列中消耗。在implementation

中明确检查了此案例
poll()

如果没有提供并发提示或除poll_one()以外的任何值,则每次都将已发布的处理程序同步到主队列中。由于不需要复制专用队列,嵌套的// We want to support nested calls to poll() and poll_one(), so any handlers // that are already on a thread-private queue need to be put on to the main // queue now. if (one_thread_) if (thread_info* outer_thread_info = ctx.next_by_key()) op_queue_.push(outer_thread_info->private_op_queue); 1调用将正常运行。


1。在networking-ts draft中,请注意,不得从当前正在调用poll()的线程调用poll_one()

答案 1 :(得分:1)

  

在嵌套或中调用asio :: io_service :: poll()或poll_one()   递归时(即从处理程序内)有效吗?

Sytaxically,这是有效的。但是,它在每个处理程序中都不好用,你应该运行poll()。此外,您的堆栈跟踪将增长到非常大的尺寸,并且您可能会遇到堆栈的大问题。