【问题标题】:Using select() then read() vs a blocking read() loop使用 select() then read() 与阻塞 read() 循环
【发布时间】:2015-03-17 19:21:41
【问题描述】:

我正在开发一个多线程服务器代码,我在其中创建了 2 个线程,它们在 accept() 之后与阻塞的 TCP 套接字交互。

线程 #1 检查是否有来自 msg 队列的命令并将其写入套接字。线程 #2 只是一个简单的 while 循环,它调用 read,然后在没有数据可供读取时阻塞。

但是我的同事告诉我,我不应该使用 read 进行 while 循环,因为它会浪费 CPU 周期(这是一个阻塞 read,内核不是让线程进入睡眠状态吗?),我应该使用select() 系统调用然后读取套接字。

我假设读取线程不正确吗?哪种方法更好?

【问题讨论】:

标签: c multithreading sockets


【解决方案1】:

您是正确的,线程在 I/O 上阻塞时不会消耗 CPU。

select() 系统调用是一个可行的替代方案,但它的主要用例是将 I/O 从/到多个通道多路复用。在这种情况下,一个线程通过阻塞读取(或写入)为所有通道提供服务是不可行的,因为它经常被阻塞以等待来自一个通道的数据,而另一个通道已经准备好并等待数据。另一方面,您不想在循环中执行非阻塞读取,因为那浪费 CPU。

至于是更喜欢阻塞read() 还是阻塞select()(或poll())来管理单个通道,这有点像苹果对橙子的比较,但如果这些是选择,那么阻止read() 更简单。这就是我会做的。苹果对苹果的比较将更多地沿着多个线程或子进程的路线,每个阻塞在read()不同的通道上,而一个进程的一个线程在select()的帮助下管理多个通道。

【讨论】:

  • 与使用 select() 进行两次系统调用然后再读取相比,简单的 read() 会不会更快?
  • @hastartes,稍微快一点,是的,但您可能不会注意到差异。这种情况下的性能肯定会受到实际数据传输的限制。
【解决方案2】:

循环查看是否有数据并不是您朋友建议的好方法。它浪费CPU时间。这种方法称为轮询,这是一种低效的方法

相反,读取应该是事件驱动的。这意味着在有数据可供读取之前,Read 线程根本不会运行。 所以,你朋友的建议是正确的。您应该转向事件驱动模型。这个所谓的 Select() 调用的作用是,它告诉 TCP 堆栈在有可用数据时通知读取线程。

因此,不是您的线程检查数据是否连续可用,而是 TCP 会在有数据时通知您。

希望清楚

【讨论】:

  • 读取循环在 I/O 上阻塞时不会消耗 CPU。轮询确实会浪费 CPU,但 OP 描述的程序没有执行轮询;这将是您通过 非阻塞 读取获得的结果。
  • select()不也是一种投票方式吗?
  • @PaŭloEbermann,没有 select() 不是一种投票形式,至少现在不是。在 80 年代末和 1990 年代初,一些 Unix 将 select() 实现为轮询循环,因为它们没有正确的底层驱动程序架构来支持适当的阻塞机制。 Cygwin 上 select() 的实现也会轮询(它为每个文件描述符设置一个线程,每个线程都会轮询可用的数据),或者至少它曾经这样做。
猜你喜欢
  • 2011-07-18
  • 1970-01-01
  • 1970-01-01
  • 2013-10-23
  • 1970-01-01
  • 1970-01-01
  • 2020-11-11
  • 1970-01-01
  • 2013-08-02
相关资源
最近更新 更多