套接字使用线程而不是 select()

Sockets use threads instead of select()

我有一个关于多插座的问题。 我知道我必须对多套接字使用 select()select() 等待 fd ... 但是,当我们可以为每个套接字创建一个线程并分别对每个套接字执行 accept() 时,为什么我们需要使用 select() 呢?这甚至是个坏主意吗?只是关于“套接字太多,线程太多所以”还是什么??

没错,您可以通过为每个套接字生成一个线程,然后在每个线程上使用阻塞 I/O 来避免多路复用套接字。

这使您不必处理 select()(或 poll() 等);但现在你必须处理多个线程,这通常更糟。

在您的特定程序中,管理线程是否比套接字多路复用更麻烦在很大程度上取决于您的程序试图做什么。例如,如果程序中的线程不需要相互 communicate/co-operate 或共享任何资源,那么多线程设计就可以很好地工作(多进程设计也是如此)。另一方面,如果您的线程都需要访问共享的数据结构或其他资源,或者如果它们需要相互交互,那么您手上就会遇到一些编程挑战,您将需要完美地解决 100% 问题,否则您最终会得到一个“似乎大部分时间都可以工作”但偶尔会死锁、崩溃或由于 incorrect/insufficient 同步而给出错误结果的程序。这种“元稳定性”现象在有缺陷的多线程程序中 common/severe 比在有缺陷的单线程程序中更多 common/severe,因为每次 运行 多线程程序的确切执行流程都会不同(由于线程相对于彼此的异步性质)。

抛开稳定性和代码正确性问题不谈,使用单线程设计可以避免其他一些多线程特有的问题:

  1. 大多数 OS 不能很好地扩展到几十个线程以上。因此,如果您正在考虑每个客户端一个线程,并且想要支持成百上千个并发客户端,那么您就会遇到一些性能问题。

  2. 很难控制在阻塞套接字调用中被阻塞的线程。假设用户按下了 Command-Q(或任何合适的等价键),那么现在是您的程序退出的时候了。如果在阻塞套接字调用中有一个或多个线程被阻塞,则没有直接的方法可以做到这一点:

    • 你不能单方面调用 exit(),因为当主线程正在拆除进程全局资源时,一个或多个线程可能仍在使用它们,导致偶尔崩溃
    • 您不能要求线程退出(通过原子布尔值或其他方式),然后调用 join() 等待它们,因为它们在 I/O 调用中阻塞,因此可能需要minutes/hours/days 在他们回应之前
    • 您无法向线程发送信号并让它们在信号处理程序中做出反应,因为信号是针对每个进程的,您无法控制哪个线程将接收信号。
    • 您不能单方面终止线程,因为它们可能持有资源(如互斥体或文件句柄),这些资源将永远保持未释放状态,可能导致死锁或其他问题
    • 您不能为它们关闭线程的套接字,并希望这会导致线程出错并终止,因为如果线程也尝试关闭这些相同的资源,这会导致竞争条件。
    • 因此,即使在多线程设计中,如果您想要干净关闭(或任何其他类型的网络线程本地控制),您通常最终不得不使用非阻塞 I/O and/or 无论如何,每个线程内的套接字多路复用,所以现在你在复杂性方面得到了两个世界中最糟糕的。