假设我们有一个基于客户端服务器的即时消息应用程序,而不是p2p。实际协议无关紧要,重要的是服务器架构。可以使用非阻塞套接字对所述服务器进行编码以在单线程,非并行模式下操作,根据定义,这允许我们立即(或即时)有效地执行读写操作。非阻塞套接字的这一特性允许我们在服务器的核心使用某种选择/轮询功能,并在实际的套接字读/写操作中立即浪费,而是花时间处理所有这些信息。根据我的理解,正确编码,这可以非常快。但是有第二种方法,那就是积极地多线程,创建一个新线程(显然使用某种线程池,因为在某些平台上和某些情况下,这种操作可能非常慢),并让这些线程并行工作,而主后台线程处理accept()和东西。我已经看到这种方法通过网络在不同的地方解释,所以它显然确实存在。
现在问题是,如果我们有非阻塞套接字,立即读/写操作,以及简单,易于编码的设计,为什么第二个变体甚至存在?我们试图用第二种设计克服哪些问题,即线程? AFAIK通常用于处理一些缓慢且可能阻塞的操作,但似乎没有这样的操作!
答案 0 :(得分:1)
我假设你不是在谈论每个客户端都有一个线程,因为这样的设计通常是出于完全不同的原因,而是一个线程池,每个线程处理几个并发客户端。
与单线程服务器相比,arcitecture的原因只是利用了多个处理器。你做的工作比单纯的I / O要多。你必须解析消息,做各种工作,甚至可能运行一些更重量级的加密算法。这一切都需要CPU。如果您想扩展,利用多个处理器将允许您扩展更多,和/或保持每个客户端的延迟更低。
在多线程环境中你可能需要更多的锁定这一事实可能有点抵消这种设计的一些好处,但是做得对,当然取决于你正在做的事情,这可能是一个巨大的胜利 - 以更复杂的代价为代价。
此外,这可能有助于克服操作系统限制。内核中的I / O路径可能会在处理器之间分配更多。并非所有操作系统都可以完全从单个线程应用程序中线程化IO。回到过去,没有旧的* nix select()的所有很好的替代方案,它通常具有1024的filedesciptor限制,并且类似的API在你告诉它监视太多套接字时严重开始降级。在多个线程或进程上传播所有这些客户端有助于克服这一限制。
对于线程之间的1:1映射,实现该架构有几个原因:
更简单的编程模型,可能导致更难找到错误,并且实施起来更快。
支持阻止API。这些都到处都是。让一个线程处理许多/所有客户端,然后继续对数据库进行阻塞调用将阻止所有人。即使读取文件也会阻止您的应用程序,并且您通常无法监视IO事件的常规文件句柄/描述符 - 或者当您可以时,编程模型通常非常复杂。
这里的缺点是它不会扩展,至少不能使用最广泛使用的语言/框架。拥有数千个本机线程会损害性能。虽然有些语言在这里提供了更轻量级的方法,例如Erlang,在某种程度上Go。