什么是最好的,单线程或多线程服务器?

时间:2011-05-11 08:45:38

标签: c linux network-programming

我必须创建一个简单的客户端< - >服务器通信,以使用C语言(Linux)传输文件。

服务器接受10000端口上的连接,我不知道是否更好地为每个请求创建一个新线程或创建固定数量的线程并使用异步技术。

CASE A:

client --> server --> (new thread) --> process the request

CASE B:

SERVER --> create thread 1 - thread 2 - thread 3

then

client1 --> server --> thread 1
client2 --> server --> thread 2
client3 --> server --> thread 3
client4 --> server --> thread 1
client5 --> server --> thread 2
client6 --> server --> thread 3

在这种情况下,线程1可以处理许多客户端的请求

我的考虑因素:

案例1:速度更快但浪费了大量内存

案例2:速度较慢但使用内存不足

我错了吗?

7 个答案:

答案 0 :(得分:5)

如果你考虑检查广泛使用的http服务器(nginx,lighttpd,apache)的体系结构你会注意到,使用固定线程数的那些(所谓的“工作线程”,它们的数量应该取决于服务器上的进程数)是很多比使用大型线程池的速度更快。但是,有非常重要的时刻:

  1. “工作者线程”的实现不应该像诱人的那样简单,否则就不会有真正的性能提升。每个线程都应该使用状态机实现每个伪并发,并及时处理多个请求。这里不允许阻塞操作 - 例如,从硬盘驱动器等待I / O获取文件内容的线程中的时间可用于解析对下一个客户端的请求。不过,编写代码非常困难。

  2. 基于线程的解决方案(具有可重用的线程池,因为线程创建是重量级操作)在考虑性能与编码时间与代码支持时是最佳的。如果您的服务器不应该每秒处理数千个请求,那么您将能够以非常自然的阻塞方式进行编码,而不会有完全失败的风险。

  3. 正如您所注意到的,“工作线程”解决方案本身仅为静态数据提供服务,它们将动态脚本执行代理到其他程序。据我所知(可能是错的),这是由于非阻塞处理请求的复杂性以及在其上下文中执行的一些未知动态内容。无论如何,当你谈到简单的文件传输时,这不应该是你的问题。

  4. 有限线程解决方案在重负载系统上更快的原因 - 线程http://en.wikipedia.org/wiki/Context_switch是相当昂贵的操作,因为它需要从寄存器保存数据并加载新的数据,只要其他一些线程本地数据。如果你有太多的线程与进程数相比(比如多1000倍),你的应用程序中的大量时间将被浪费,只需在线程之间切换。

    因此,对您的问题的简短回答是:“不,它与内存使用无关,选择的是所提供的数据类型,计划的请求/秒计数以及在编码上花费大量时间的能力”。

答案 1 :(得分:2)

这里没有正确答案。取决于很多事情。你需要自己选择。

“案例1:速度快但浪费了大量记忆。” “情况2:速度较慢,但​​使用的内存较低”

错误。取决于很多事情。创建线程并不是那么昂贵(虽然但不是那么多),但如果线程太多,你就会遇到问题。

这很大程度上取决于负载 - 预期负载是多少?如果它是,比方说,每秒大约1000个请求 - 你知道,如果你每秒创建1000个线程......这将是灾难:D

另外 - 创建尽可能多的线程,因为CPU可以处理它们,而不需要(很多)在它们之间切换。有一个很大的机会(当然取决于你的程序),一个单核CPU工作得多,10个线程慢得多,而不是1个(或2个)。这实际上取决于这些线程的作用。

我选择创建线程池并重用线程。

答案 2 :(得分:2)

我的第一选择是使用select(2)进行单线程。 如果这在性能方面不够好,我会使用线程池解决方案。 它会更好地扩展。

有时候每个客户端创建一个线程是完全可以的。 我已经做到了,它适用于该应用程序,通常有大约100个客户端,最多1000个客户端。那是15年前的事了。今天,由于更好的硬件,相同的应用程序可以处理10000个客户端。请注意,每个客户端的一个线程不能很好地扩展。

答案 3 :(得分:1)

我知道你问这个问题已经有一段时间了,但是从我已经用C编写了一些服务器的人的角度来看我的问题。

如果您的服务器是您自己的,完全依赖并且不依赖于其他代码,我会高度建议您使用非阻塞单线程套接字和使用epoll(Linux),kqueue(BSD)或WSAEventSelect(Windows)。

这可能要求你拆分一个原本本来就很简单的代码"对于更小的块,但如果可扩展性是您正在寻找的,那么这将击败任何基于线程/基于选择的服务器。

曾经有一篇很棒的文章被称为" C10K问题"这完全集中在如何处理10,000个并发连接的问题上。我实际上从中学到了很多东西,以下是它的链接:http://www.kegel.com/c10k.html

还有另一篇关于可扩展性的伟大文章称为"可扩展网络"你可以在这里找到:http://bulk.fefe.de/scalable-networking.pdf

这两个是伟大的读物,希望有所帮助。

答案 4 :(得分:0)

完全归结为你。没有正确或错误的答案。你已经确定了两者的优点和缺点,你对这两者都是正确的; 1更快但更密集,2更慢,因为客户可能不得不等待。

答案 5 :(得分:0)

我会使用预先创建的线程池,并在完成当前正在处理的请求时重新使用它们。创建线程可能很昂贵,因为它主要涉及对内核的调用。

使用pthreads有“threadpool”类型项目here。也许你可以从那里得到一些关于如何实施的想法。

答案 6 :(得分:0)

真的取决于您的服务器正在做什么。

我建议你做最简单的事情。这可能是一个单进程模型,它使用,select,poll,libevent或类似方式多路复用所有可用连接。

也就是说,如果你正在使用TCP。

如果您使用UDP,它会更容易,因为应用程序可以使用一个套接字执行所有操作,因此它可以(可能)使用阻塞套接字。