Asynchronous 什么';epoll、poll和threadpool之间的区别是什么?

Asynchronous 什么';epoll、poll和threadpool之间的区别是什么?,asynchronous,epoll,io-completion-ports,Asynchronous,Epoll,Io Completion Ports,有人能解释一下epoll、poll和threadpool之间的区别吗 利/弊是什么 对框架有什么建议吗 对简单/基本教程有什么建议吗 似乎epoll和poll是Linux特有的。。。Windows是否有同等的替代方案 Threadpool实际上与poll和epoll不属于同一类别,因此我假设您在“Threadpool处理多个连接,每个连接一个线程”中引用Threadpool 利弊 线程池 对于中小型并发来说相当有效,甚至可以胜过其他技术 使用多核 即使某些系统(例如Linux)原则上可以调

有人能解释一下
epoll
poll
和threadpool之间的区别吗

  • 利/弊是什么
  • 对框架有什么建议吗
  • 对简单/基本教程有什么建议吗
  • 似乎
    epoll
    poll
    是Linux特有的。。。Windows是否有同等的替代方案

Threadpool实际上与poll和epoll不属于同一类别,因此我假设您在“Threadpool处理多个连接,每个连接一个线程”中引用Threadpool

利弊
  • 线程池
    • 对于中小型并发来说相当有效,甚至可以胜过其他技术
    • 使用多核
    • 即使某些系统(例如Linux)原则上可以调度100000个线程,也不能扩展到“几百个”之外
    • 幼稚的实现显示出“”问题
    • 除了语境转换和雷鸣群集之外,人们必须考虑记忆。每个线程都有一个堆栈(通常至少有一兆字节)。因此,一千个线程仅为堆栈占用一GB的RAM。即使内存没有提交,在32位操作系统下它仍然会占用大量的地址空间(在64位操作系统下这不是一个真正的问题)
    • 线程实际上可以使用
      epoll
      ,尽管显而易见的方法(所有线程阻塞
      epoll\u wait
      )是没有用的,因为epoll会唤醒等待它的每个线程,所以它仍然会有相同的问题。
      • 最佳解决方案:单线程侦听epoll,进行输入多路复用,并将完整的请求传递给线程池
      • futex
        是您在这里的朋友,与每个线程的快进队列相结合。尽管文档记录糟糕且笨拙,
        futex
        提供了所需的功能
        epoll
        可以一次返回多个事件,
        futex
        可以让您高效、精确地控制一次唤醒N个阻塞的线程(N为
        min(num\u cpu,num\u events)
        ),在最好的情况下,它根本不涉及额外的系统调用/上下文切换
      • 实施起来并不琐碎,需要一些注意
  • fork
    (又称旧式线程池)
    • 对于中小型并发来说相当有效
    • 规模远远超过“几百”
    • 上下文开关要昂贵得多(不同的地址空间!)
    • 在fork更昂贵的旧系统(所有页面的深度副本)上,扩展明显更差。即使在现代系统上,fork也不是“免费的”,尽管开销主要由写时拷贝机制合并而成。在同样被修改的大型数据集上,
      fork
      之后的大量页面错误可能会对性能产生负面影响
    • 然而,在超过30年的时间里,它被证明是可靠的
    • 实施起来非常容易,而且坚如磐石:如果其中任何一个进程崩溃,世界不会结束。你(几乎)什么都做不了
    • 非常容易出现“雷鸣族”
  • 投票
    /
    选择
    • 两种味道(BSD和System V)或多或少是相同的
    • 虽然使用起来有些陈旧、缓慢、有些笨拙,但几乎没有一个平台不支持它们
    • 等待一组描述符上的“发生了什么”
      • 允许一个线程/进程一次处理多个请求
      • 没有多核使用
    • 每次等待时,需要将描述符列表从用户复制到内核空间。需要对描述符执行线性搜索。这限制了它的有效性
    • 不能很好地扩展到“数千”(事实上,在大多数系统上,硬限制在1024左右,或者在某些系统上低至64)
    • 使用它是因为如果您只处理十几个描述符(没有性能问题),或者您必须支持没有更好的平台,那么它是可移植的。不要用其他方式
    • 从概念上讲,服务器比分叉服务器稍微复杂一些,因为现在需要维护许多连接和每个连接的状态机,并且必须在请求传入时在请求之间进行多路复用,组装部分请求,等等。简单的分叉服务器只知道一个套接字(第二,计算侦听套接字),读取直到得到它想要的或连接半关闭,然后写入它想要的任何内容。它不担心阻塞、准备就绪或饥饿,也不担心一些不相关的数据进入,这是其他进程的问题
  • epoll
    • 仅限Linux
    • 昂贵的修改与高效等待的概念:
      • 添加描述符时,将有关描述符的信息复制到内核空间(
        epoll\u ctl
        • 这通常是很少发生的事情
      • 等待事件时不需要将数据复制到内核空间(
        epoll\u wait
        • 这通常是经常发生的事情
      • 将服务员(或者更确切地说是它的epoll结构)添加到描述符的等待队列中
        • 所以说话人知道谁在听,并在适当的时候直接向服务员发出信号,而不是让服务员搜索说话人列表
        • poll
          工作原理的相反方式
        • O(1)在描述符数量方面具有小k(非常快),而不是O(n)
    • timerfd
      eventfd
      配合使用效果非常好(计时器分辨率和精确度也令人惊叹)
    • signalfd
      配合良好,消除了信号处理的笨拙,使其以非常优雅的方式成为正常控制流程的一部分。
    • 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();
      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();