以嵌套或递归方式(即从处理程序内)调用asio :: io_service :: poll()或poll_one()是否有效?
一个真正基本的测试似乎暗示这是有效的(我只在一个平台上完成了测试)但我想确保从处理程序中再次调用poll()被认为是有效的行为.
我在asio文档中找不到任何相关信息,所以我希望那些对asio内部工作有更多经验的人可以通过解释或引用来验证这一点.
基本测试:
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 } }
Run Code Online (Sandbox Code Playgroud)
这是有效的.
对于处理的函数族io_service,run()是唯一有限制的函数:
该
run()功能不能从当前调用的一个线程调用run(),run_one(),poll()或poll_one()在同一个io_service对象.
但是,我倾向于认为文档也应该包含相同的注释run_one(),因为嵌套调用可能导致它无限期地阻塞以下任何一种情况[1]:
io_service是当前正在执行的处理程序io_service具有并发提示1对于Windows I/O完成端口,在为io_service使用提供服务的所有线程中执行解复用GetQueuedCompletionStatus().在高级别,线程调用GetQueuedCompletionStatus()函数就好像它们是线程池的一部分一样,允许操作系统将工作分派给每个线程.由于没有单个线程负责将操作解复用到其他线程,嵌套调用poll()或poll_one()不影响其他线程的操作调度.该文件规定:
解复用I/O完成端口在执行所有调用线程
io_service::run(),io_service::run_one(),io_service::poll()或io_service::poll_one().
对于所有其他解复用机制系统,单线程服务io_service用于解复用I/O操作.精确的解复用机制可以在特定于平台的实现说明中找到:
使用解复用[
/dev/poll,epoll,kqueue,select]在执行一个调用的线程io_service::run(),io_service::run_one(),io_service::poll()或io_service::poll_one().
解复用机制的实现略有不同,但在高级别:
io_service有一个主队列,线程从该队列中消耗准备运行的操作io_service都会在堆栈上创建一个私有队列,用于以无锁方式管理操作当io_service被构建,它可以提供并发提示,提示的实施应该有多少个线程允许同时运行.当非I/O完成端口实现提供并发提示时1,它们被优化为尽可能多地使用专用队列并推迟与主队列的同步.例如,通过post()以下方式发布处理程序时:
io_service保证线程安全,以便在排队处理程序之前锁定主队列.当嵌套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);
Run Code Online (Sandbox Code Playgroud)
如果没有提供并发提示或除1提供的任何其他值,则每次都将已发布的处理程序同步到主队列中.由于不需要复制专用队列,嵌套poll()和poll_one()调用将正常运行.
1.在networking-ts草案中,注意run_one()不能从当前调用的线程调用run().