套接字使用线程而不是 select()
Posted
技术标签:
【中文标题】套接字使用线程而不是 select()【英文标题】:Sockets use threads instead of select() 【发布时间】:2021-12-14 19:33:10 【问题描述】:我有一个关于多插座的问题。
我知道我必须将select()
用于多套接字。 select()
等待 fd
...
但是,当我们可以为每个套接字创建一个线程并在每个套接字上分别执行 accept()
时,为什么我们需要使用 select()
呢?这甚至是一个坏主意吗?只是关于“太多的套接字,太多的线程”还是什么??
【问题讨论】:
【参考方案1】:确实,您可以通过为每个套接字生成一个线程,然后在每个线程上使用阻塞 I/O 来避免多路复用套接字。
这使您不必处理select()
(或poll()
等);但是现在您必须改为处理多个线程,这通常会更糟。
在您的特定程序中,线程是否比套接字多路复用更麻烦在很大程度上取决于您的程序试图做什么。例如,如果程序中的线程不需要相互通信/协作或共享任何资源,那么多线程设计可以很好地工作(就像多进程设计一样)。另一方面,如果您的线程都需要访问共享数据结构或其他资源,或者如果它们需要相互交互,那么您将面临一些编程挑战,您需要100% 完美地解决,否则你最终会得到一个“似乎大部分时间都在工作”的程序,但由于不正确/不充分的同步,偶尔会出现死锁、崩溃或给出不正确的结果。这种“元稳定性”现象在有缺陷的多线程程序中比在有缺陷的单线程程序中更为常见/严重,因为每次运行多线程程序的确切执行流程都会有所不同(由于线程相对于彼此)。
除了稳定性和代码正确性问题之外,使用单线程设计还可以避免一些其他多线程特有的问题:
大多数操作系统不能很好地扩展到几十个线程以上。因此,如果您正在考虑每个客户端一个线程,并且希望同时支持数百或数千个客户端,那么您会遇到一些性能问题。
很难控制在阻塞套接字调用中被阻塞的线程。假设用户按下了 Command-Q(或任何适当的等效项),那么现在是时候让您的程序退出了。如果您在阻塞套接字调用中阻塞了一个或多个线程,则没有直接的方法可以做到这一点:
不能单方面调用exit(),因为当主线程正在拆除进程全局资源时,一个或多个线程可能仍在使用它们,从而导致偶尔崩溃 您不能要求线程退出(通过 atomic-boolean 或其他方式)然后调用join()
等待它们,因为它们在 I/O 调用内部阻塞,因此可能需要几分钟/几小时/几天他们回应
您无法向线程发送信号并让它们在信号处理程序中做出反应,因为信号是每个进程的,您无法控制哪个线程将接收信号。
您不能单方面终止线程,因为它们可能持有的资源(如互斥锁或文件句柄)将永远未释放,从而可能导致死锁或其他问题
您不能为它们关闭线程的套接字,并希望这会导致线程出错并终止,因为如果线程也尝试关闭这些相同的资源,这会导致竞争状况。李>
因此,即使在多线程设计中,如果您想要彻底关闭(或任何其他类型的网络线程本地控制),您通常最终不得不在每个内部使用非阻塞 I/O 和/或套接字多路复用无论如何,线程,所以现在你已经得到了两全其美的复杂性。
【讨论】:
Re, "...不要扩展到几十个以上..." 我花了几年时间对一个商业产品进行维护,在实际实践中,它运行在 200 到 300 个线程之间大型安装中的每台主机。我们为 Windows Server 和 Linux 构建了版本。我曾经与博士计算机科学家争论过,他们设计了我们是否可以或应该重新架构它以使用更少的线程。我从来没有赢过。而且,我从来没有赢过的部分原因是该死的东西确实起作用了。 我无法与 实际工作 争论......但是如果你继续添加线程,在某些时候你会“碰壁”,你的电脑会花费更多与实际工作相比,进行上下文切换的时间,并且您需要重新架构。关于这个主题的经典文章在这里:kegel.com/c10k.html以上是关于套接字使用线程而不是 select()的主要内容,如果未能解决你的问题,请参考以下文章