C read()线程安全吗?

时间:2015-03-29 16:24:08

标签: c linux multithreading file thread-safety

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

要实现这一点,我需要使用一个API,它为我想要读取的文件提供了一个文件描述符。我正在使用C的read函数读取文件的块。手册页说:“成功时,返回读取的字节数(零表示文件结束),文件位置按此编号提前。”但是,我找不到任何确定的信息,关于文件位置的提升是否是线程安全的。

假设我有线程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。正在读取文件的线程然后根据客户端请求获取该文件描述符。如果每个线程都存储自己关于文件位置的信息,那么应该没问题。我找不到任何有关文件位置的信息,read()在哪里找出它是什么。

3 个答案:

答案 0 :(得分:4)

read本身是线程安全的,但这并不一定意味着你想用它做的事情是线程安全的。每POSIX(2.9.7 Thread Interactions with Regular File Operations):

  

当对常规文件或符号链接进行操作时,以下所有函数在POSIX.1-2008中指定的效果中应相互原子化:

     

...

read位于随后的列表中。)

除此之外,这意味着读取数据和推进当前文件位置相对于彼此是原子的,并且读取的每个字节将被准确读取一次。但是,还有其他一些因素会让您感到困惑,尤其是:

  • 短读取:read(fd, buf, n)无需读取n个字节。它可以读取1到n字节之间的任何值,当你再次调用它来读取余数时,第二次读取不再是第一次读取的原子。

  • 其他文件类型:POSIX仅保证常规文件的read的原子性以及其他一些类型。像Linux 这样的特定系统可能有更强的保证,但我会谨慎。

最好使用pread函数(您可以在其中指定文件偏移量,以便从中读取,而不得不寻找该位置,并且结果文件位置保持不变)或对文件访问执行锁定以避免此类问题。

答案 1 :(得分:0)

注意read

  

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

来自同一参考:

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

...

     

pread()函数应等效于read(),除了它应从文件中的给定位置读取而不更改文件指针。 pread()的前三个参数与read()相同,并为文件中的所需位置添加了第四个参数偏移量。尝试对无法搜索的文件执行pread()将导致错误。

每个帖子都可以跟踪自己的offset,并指定它。

答案 2 :(得分:0)

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

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是线程安全的,如果调用它的两个线程也从不处理相同的数据两次。