可能有人解释的区别是什么之间epoll
,poll
和线程池?
- 优点/缺点是什么?
- 对框架有什么建议吗?
- 对简单/基础教程有什么建议吗?
- 似乎
epoll
并且poll
特定于Linux ... Windows是否有等效的替代品?
Answers:
线程池与民意测验和epoll确实不属于同一类别,因此我假设您所指的线程池与“线程池以每个连接处理一个线程的方式处理多个连接”中的线程池相同。
epoll
,尽管显而易见的方法(所有线程都在上阻塞epoll_wait
)是没有用的,因为epoll会唤醒等待它的每个线程,因此仍然会有相同的问题。
futex
是您的朋友吗,例如每个线程一个快速转发队列。尽管文档记录不清且笨拙,但futex
它确实提供了所需的内容。epoll
可能一次返回多个事件,并futex
让您高效且以精确控制的方式一次唤醒N个阻塞的线程(min(num_cpu, num_events)
理想情况下为N ),并且在最佳情况下,它根本不涉及额外的syscall / context开关。fork
(又名旧时尚线程池)
fork
上也不是“免费”的,尽管开销大部分是由写时复制机制合并的。在也被修改的大型数据集上,随后的大量页面错误fork
可能会对性能产生负面影响。poll
/ select
epoll
epoll_ctl
)
epoll_wait
)
poll
工作方式timerfd
和搭配使用时效果很好eventfd
(计时器分辨率和准确性也很惊人)。signalfd
,消除了对信号的笨拙处理,使它们以非常优雅的方式成为常规控制流的一部分。eventfd
,但需要(迄今为止)未记录的功能。poll
可能相同或更好。epoll
不能做“魔术”,也就是说,相对于发生的事件数,仍然必须为O(N)。epoll
由于新的recvmmsg
syscall一次返回多个准备就绪通知(尽可能多,直到您指定为maxevents
),因此可以很好地发挥作用。这样就可以在繁忙的服务器上通过一个syscall接收到15条EPOLLIN通知,并通过第二个syscall读取对应的15条消息(syscall减少了93%!)。不幸的是,一次recvmmsg
调用的所有操作都引用同一个套接字,因此它对于基于UDP的服务最有用(对于TCP,将必须有一种recvmmsmsg
syscall,每项还需要一个套接字描述符!)。EAGAIN
即使在使用时epoll
也应进行检查,因为在某些特殊情况下,epoll
报告准备情况和后续的读取(或写入)操作仍会阻塞。在某些内核上,poll
/也是如此select
(尽管它已被修复)。EAGAIN
收到通知后返回时,可以无限地从快速发送者读取新的传入数据,而完全饿死了慢速发送者(只要数据保持足够快的速度,您可能会EAGAIN
在很长一段时间内看不到! )。以同样的方式适用于poll
/ select
。epoll_wait
以来(或从打开描述符开始,如果没有先前调用),是否发生了IO活动。epoll_wait
,这表明由于IO活动已经发生了人过去叫任 epoll_wait
或描述符上的读/写功能,此后仅向调用或已阻塞的下一个线程 再次报告准备情况,以防止有人在描述符上调用读(或写)功能epoll_wait
后发生任何操作。” ,也...文档中所建议的不完全是。kqueue
epoll
,不同的用法,效果相似。libevent -2.0版本还支持Windows下的完成端口。
ASIO-如果您在项目中使用Boost,则别无所求:您已经可以将其作为boost-asio使用。
上面列出的框架带有大量的文档。Linux文档和MSDN广泛解释了epoll和完成端口。
使用epoll的迷你教程:
int my_epoll = epoll_create(0); // argument is ignored nowadays
epoll_event e;
e.fd = some_socket_fd; // this can in fact be anything you like
epoll_ctl(my_epoll, EPOLL_CTL_ADD, some_socket_fd, &e);
...
epoll_event evt[10]; // or whatever number
for(...)
if((num = epoll_wait(my_epoll, evt, 10, -1)) > 0)
do_something();
IO完成端口的迷你教程(请注意使用不同的参数两次调用CreateIoCompletionPort):
HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0); // equals epoll_create
CreateIoCompletionPort(mySocketHandle, iocp, 0, 0); // equals epoll_ctl(EPOLL_CTL_ADD)
OVERLAPPED o;
for(...)
if(GetQueuedCompletionStatus(iocp, &number_bytes, &key, &o, INFINITE)) // equals epoll_wait()
do_something();
(这些小型工具忽略了所有类型的错误检查,希望我没有做任何错别字,但在大多数情况下它们应该可以使您有所了解。)
编辑:
请注意,完成端口(Windows)在概念上可以作为epoll(或kqueue)以其他方式工作。顾名思义,它们表示完成而不是准备。也就是说,您触发一个异步请求,然后将其忽略,直到一段时间后,您被告知该请求已完成(成功或不十分成功,并且还有“立即完成”的例外情况)。
使用epoll,您将阻塞直到通知您“某些数据”(可能只有一个字节)到达并且可用,或者有足够的缓冲区空间,因此您可以进行写操作而不会阻塞。只有到那时,您才开始实际操作,然后希望该操作不会阻塞(除了您期望的那样,没有严格的保证—因此,将描述符设置为非阻塞并检查EAGAIN [EAGAIN和EWOULDBLOCK是一个好主意。对于套接字,因为很高兴,所以标准允许两个不同的错误值])。
min(num_cpu, num_events)
“ futex”描述中的意思吗?
min
,不是max
-我会解决错字。谢谢。