C read() 线程安全吗?
Is C read() Thread Safe?
我正在编写一个程序,其中多个线程可能同时从一个文件中读取。没有线程正在写入文件,但它们可能各自将其内容复制到单独的内存段。
要执行此操作,我需要使用 API 为我要读取的文件提供文件描述符。我正在使用 C 的 read
函数读取文件块。手册页说,"On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number." 不过,关于文件位置的推进是否线程安全,我找不到任何确定的信息。
假设我有线程 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
...
如果有帮助,每个线程将使用相同的文件描述符。换句话说,是 API 使用 open()
打开文件。正在读取文件的线程然后根据客户端请求获取该文件描述符。如果每个线程都存储自己的文件位置信息,那么应该没问题。我只是找不到任何关于保存文件位置的信息,以及 read()
找出它的位置。
备注read:
The read()
function shall attempt to read nbyte
bytes from the file associated with the open file descriptor, fildes
, into the buffer pointed to by buf
. The behavior of multiple concurrent reads on the same pipe, FIFO, or terminal device is unspecified. (emphasis mine)
来自同一参考文献:
ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset);
...
The pread()
function shall be equivalent to read()
, except that it shall read from a given position in the file without changing the file pointer. The first three arguments to pread()
are the same as read()
with the addition of a fourth argument offset for the desired position inside the file. An attempt to perform a pread()
on a file that is incapable of seeking shall result in an error.
每个线程都可以跟踪自己的offset
,并指定它。
read
本身是线程安全的,但这并不一定意味着你想用它做的事情是线程安全的。每 POSIX (2.9.7 Thread Interactions with Regular File Operations):
All of the following functions shall be atomic with respect to each other in the effects specified in POSIX.1-2008 when they operate on regular files or symbolic links:
...
(read
在后面的列表中。)
除其他外,这意味着读取数据和推进当前文件位置是彼此原子的,并且读取的每个字节将被恰好读取一次。但是,还有其他考虑因素可能会使事情复杂化,尤其是:
短读:read(fd, buf, n)
不需要读n
字节。它可以读取 1 到 n
字节之间的任何位置,并且当您再次调用它来读取剩余部分时,第二次读取相对于第一次读取不再是原子的。
其他文件类型:POSIX 仅保证 read
常规文件和其他一些类型的原子性。 Linux可能这样的特定系统有更强的保证,但我会谨慎。
最好使用 pread
函数(您可以在其中指定要从 读取的文件偏移量,而无需 查找该位置,并且结果文件位置保持不变)或对文件访问进行锁定以避免此类问题。
您对线程安全的理解不正确或应用不当。
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
是线程安全的。
我正在编写一个程序,其中多个线程可能同时从一个文件中读取。没有线程正在写入文件,但它们可能各自将其内容复制到单独的内存段。
要执行此操作,我需要使用 API 为我要读取的文件提供文件描述符。我正在使用 C 的 read
函数读取文件块。手册页说,"On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number." 不过,关于文件位置的推进是否线程安全,我找不到任何确定的信息。
假设我有线程 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
...
如果有帮助,每个线程将使用相同的文件描述符。换句话说,是 API 使用 open()
打开文件。正在读取文件的线程然后根据客户端请求获取该文件描述符。如果每个线程都存储自己的文件位置信息,那么应该没问题。我只是找不到任何关于保存文件位置的信息,以及 read()
找出它的位置。
备注read:
The
read()
function shall attempt to readnbyte
bytes from the file associated with the open file descriptor,fildes
, into the buffer pointed to bybuf
. The behavior of multiple concurrent reads on the same pipe, FIFO, or terminal device is unspecified. (emphasis mine)
来自同一参考文献:
ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset);
...
The
pread()
function shall be equivalent toread()
, except that it shall read from a given position in the file without changing the file pointer. The first three arguments topread()
are the same asread()
with the addition of a fourth argument offset for the desired position inside the file. An attempt to perform apread()
on a file that is incapable of seeking shall result in an error.
每个线程都可以跟踪自己的offset
,并指定它。
read
本身是线程安全的,但这并不一定意味着你想用它做的事情是线程安全的。每 POSIX (2.9.7 Thread Interactions with Regular File Operations):
All of the following functions shall be atomic with respect to each other in the effects specified in POSIX.1-2008 when they operate on regular files or symbolic links:
...
(read
在后面的列表中。)
除其他外,这意味着读取数据和推进当前文件位置是彼此原子的,并且读取的每个字节将被恰好读取一次。但是,还有其他考虑因素可能会使事情复杂化,尤其是:
短读:
read(fd, buf, n)
不需要读n
字节。它可以读取 1 到n
字节之间的任何位置,并且当您再次调用它来读取剩余部分时,第二次读取相对于第一次读取不再是原子的。其他文件类型:POSIX 仅保证
read
常规文件和其他一些类型的原子性。 Linux可能这样的特定系统有更强的保证,但我会谨慎。
最好使用 pread
函数(您可以在其中指定要从 读取的文件偏移量,而无需 查找该位置,并且结果文件位置保持不变)或对文件访问进行锁定以避免此类问题。
您对线程安全的理解不正确或应用不当。
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
是线程安全的。