C read()线程安全吗?

我正在编写一个程序,其中多个线程可能同时从一个文件读取。 没有线程正在写入文件,但它们可能会将其内容复制到单独的内存段中。

要做到这一点,我需要使用一个API给我一个文件描述符我想读的文件。 我用C的readfunctionread文件的块。 手册页上说:“成功时返回读取的字节数(零表示文件结束),文件位置由此数字提前”。 但是,我不能find任何明确的信息,但是文件位置的提升是否是线程安全的。

假设我有线程T1和线程T2一次读取文件的1个字节。 如果read()是线程安全的,我会期待以下内容:

 T1: read() => file position == 1 T2: read() => file position == 1 T1: read() => file position == 2 T2: read() => file position == 2 ... 

但是我担心,如果不是线程安全的,那么可能会发生以下情况:

 T1: read() => file position == 1 T2: read() => file position == 2 T1: read() => file position == 3 T2: read() => file position == 4 ... 

如果有帮助,每个线程将使用相同的文件描述符。 换句话说,就是使用open()打开文件的API。 正在读取该文件的线程将根据客户端请求获取该文件描述符。 如果每个线程都存储自己关于文件位置的信息,那么应该没问题。 我只是无法find有关文件位置的任何信息,而read()指出它是什么。

read本身是线程安全的,但这并不意味着你想要做的事情是线程安全的。 每个POSIX( 2.9.7与常规文件操作的线程交互 ):

在POSIX.1-2008规定的作用于常规文件或符号链接的效果中,下列所有功能应相互原子化:

read在下面的列表中。)

除此之外,这意味着读取数据和推进当前文件的位置是相互原子的,读取的每个字节只能被读取一次。 但是,还有其他的考虑可能会使你的工作复杂化,特别是:

  • 短读: read(fd, buf, n)不需要读取n个字节。 它可以读取1到n个字节之间的任何地方,当再次调用它来读取其余部分时,第二次读取与第一次读取不再是原子的。

  • 其他文件类型:POSIX只保证read常规文件和其他类型的原子性。 像Linux这样的具体系统可能有更强的保证,但我会谨慎。

使用pread函数(您可以指定一个文件偏移量来读取, 不必寻找该位置,以及生成的文件位置保持不变),或者在您的文件访问周围执行锁定以避免此类问题。

注意事项:

read()函数将尝试从与打开的文件描述符fildes关联的文件中读取nbyte字节到buf未指定同一管道,FIFO或终端设备上多个并发读取的行为。 (重点是我的)

从相同的参考:

 ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset); 

pread()函数应该等同于read() ,除了它应该从文件中的给定位置读取而不改变文件指针。 pread()的前三个参数与pread()相同,在文件内部添加了第四个参数偏移量。 试图在无法找到的文件上执行pread()将导致错误。

每个线程可以跟踪自己的offset ,并指定它。

您对线程安全的理解不正确,或者您误用了它。

 T1: read() => file position == 1 T2: read() => file position == 1 T1: read() => file position == 2 T2: read() => file position == 2 ... 

在这里,我们让T1和T2同时调用read ,并且不管read操作发生的顺序如何,都不可能发生结果。这是当函数不是线程安全时发生的竞争的典型示例。

粗略地说,一个函数是线程安全的,如果同时调用它会产生一个合理的结果,相当于你在非同时调用的时候得到的结果。 如果一个线程调用read从不处理相同的数据两次,那么如果两个调用它的线程也不会处理两次相同的数据,则read是线程安全的。