两个线程同时从文件描述符读取数据

11
  1. 我的问题: 在Linux(和FreeBsd以及通常的UNIX中),是否允许/合法同时从两个线程读取单个文件描述符?

  2. 我做了一些搜索,但没有找到任何相关内容,尽管很多人询问像读/写socket fd时同时进行读取的问题(意味着一个线程在写入时另一个线程在读取,而不是两个线程都在读取)。我也阅读了一些man手册,但没有得到清晰的答案。

  3. 为什么我会问这个问题。我尝试实现一个简单的程序来计算stdin中的行数,类似于wc -l。我实际上是在测试我自己制作的C ++ io引擎的开销,并发现wc比它快1.7倍。我精简了一些C ++代码并接近了wc的速度,但仍然达不到。然后我尝试使用输入缓冲区大小进行实验,对其进行优化,但仍然比wc明显慢一些。最后,我创建了2个线程并行读取相同的STDIN_FILENO,这样最终比wc更快!但行数统计变得不正确......所以我认为一些意外的junk出现在读取中。内核不关心哪个进程进行读取吗?

编辑:我做了一些研究,并发现直接通过系统调用调用read并不会改变任何东西。内核代码似乎会进行一些同步处理,但是我并没有完全理解(read_write.c)。


2
顺便说一下:并行读取可能会使您变得更慢而不是更快。如果您有I/O瓶颈,那么您所做的就是创建锁争用。您可以转而专注于缓冲策略。 - rlibby
但我没有锁定,只是读取,内核应该在内部锁定。关于缓冲:我尝试改变缓冲区大小并找到了一些最佳值。它仍然比wc慢。我现在有一个理论,我需要非阻塞读取来提高性能。 - jarero
1
非阻塞读取也没有帮助。嗯。。 - jarero
非阻塞读取 + 缓冲区大小为8页 + std::count 最终击败了 wc -l。不过,那并不完全是我的问题。 - jarero
使用适当的锁定,2个线程比最佳单线程版本快20%。由于我只有2个核心,所以没有必要测试更多的线程。 - jarero
我们能否更清楚地表明这个问题和一些答案严格限于“管道、FIFO或终端设备”,而对于普通文件,答案可能会有所不同? - dreua
3个回答

6

这是未定义的行为,POSIX说:

read()函数将尝试从与打开文件描述符fildes相关联的文件中读取nbyte字节,并将其存储到buf指向的缓冲区中。在同一管道、FIFO或终端设备上进行多个并发读操作的行为是未指定的。


1
POSIX 将需要根据每个操作系统的观察来定义。随着固态硬盘的普及,我看到越来越多这样大胆并发读取发现者的出现 :) 因为 I/O 不再是瓶颈(或者至少不是那么明显)。 - Pavel Zdenek
2
然而,read() 在普通文件和符号链接上的行为是被定义的。请参见我的回答。 - hagello

5
关于同时访问单个文件描述符(即从多个线程甚至进程)的问题,我将引用POSIX.1-2008 (IEEE Std 1003.1-2008),第2.9.7节线程与常规文件操作的交互
“2.9.7 线程与常规文件操作的交互 当它们在常规文件或符号链接上操作时,下列所有函数在 POSIX.1-2008 中指定的影响方面应该是原子的: […] read() […] 如果两个线程分别调用这些函数中的一个,每次调用都应该看到另一次调用的所有指定影响或没有其中任何一个。”
乍一看,这看起来相当不错。但是,希望您没有错过限制条件当它们在常规文件或符号链接上操作时
@jarero 引用:
“对于同一管道、FIFO 或终端设备上的多个并发读取的行为未指定。”
那么,我想你已经默认同意了:这取决于你正在读取的文件类型。你说,你从 STDIN 读取。嗯,如果你的 STDIN 是一个纯文件,你可以使用并发访问。否则你不应该这样做。

4

当使用一个描述符(fd)时,read()和write()会依赖fd的内部状态来确定读和写的"当前偏移量"。因此,它们不是线程安全的。

为了允许单个描述符被多个线程同时使用,提供了pread()和pwrite()接口。使用这些接口,指定描述符和所需的偏移量,因此不使用描述符中的"当前偏移量"。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接