一个程序可以同时在同一个 FILE* 上调用 fflush() 吗?
Can a program call fflush() on the same FILE* concurrently?
如果多个线程在同一个 FILE*
变量上同时调用 fflush()
,是否会发生任何不好的事情(如未定义行为、文件损坏等)?
澄清:我并不是说同时写入文件。我的意思是同时刷新它。
线程不会并发读取或写入文件(它们只在临界区内写入文件,一次一个线程)。他们只刷新临界区之外,尽快释放临界区,让其他人做其他工作(文件写入除外)。
尽管可能发生一个线程正在写入文件(在临界区内),而另一个线程正在刷新文件(在临界区外)。
答案很简单,您可能不会那样做,因为该文件只有一个 "current position"。你如何跟踪它?文件打开是为了顺序访问还是随机访问?在后一种情况下,您可以多次打开它(每个线程一次),然后设计保持文件结构一致的方法。
您不应该在输入流上调用 fflush()
,它会调用未定义的行为,因此我假设该流以写入或更新模式打开。
如果流以更新模式打开("w+"
或 "r+"
),调用 fflush()
时最后的操作不能是读取。由于流在各种线程中异步使用,如果没有某种形式的进程间通信和同步或锁定,如果您进行任何读取,将很难确保这一点。在更新模式下打开文件仍有正当理由,但请确保在启动 fflush
线程后不进行任何读取。
fflush()
不修改当前位置。它只会导致将任何缓冲输出写入系统。流通常受锁保护,因此在一个线程中调用 fflush()
不应弄乱另一个线程执行的输出,但它可能会改变系统写入的时间。如果多个线程输出相同的 FILE*
,交错发生的顺序无论如何都是不确定的。此外,如果您使用 fseek()
是同一流的不同线程,您 必须 使用自己的锁以确保 fseek()
和以下输出之间的一致性.
虽然看起来可以,但可能不推荐这样做。您可以在每个线程中的写操作之后,在释放锁之前调用 fflush()
。
C1 中的流是线程安全的2。在访问流之前需要函数锁定流3。
fflush 函数是线程安全的,可以随时从任何线程调用,只要该流是输出流或更新流4.
1 根据当前标准,即 C11。
2(引自:ISO/IEC 9899:201x 7.21.3 Streams 7)
每个流都有一个关联的锁,用于在多个流时防止数据竞争
执行线程访问流,并限制流操作的交错
由多个线程执行。一次只有一个线程可以持有此锁。锁是
可重入:单个线程可能在给定时间多次持有锁。
3(引自:ISO/IEC 9899:201x 7.21.3 Streams 8)
所有读取、写入、定位或查询流位置的函数都会锁定流
在访问它之前。他们在访问完成时释放与流关联的锁
完全的。
可重入:单个线程可能在给定时间多次持有锁。
4(引自:ISO/IEC 9899:201x 7.21.5.2 fflush函数2)
如果 stream 指向输出流或更新流,其中最新的
未输入操作,fflush 函数会导致该流的任何未写入数据
传送到宿主环境写入文件;否则,行为是
未定义。
POSIX.1 和对字符流(由指向 FILE 类型对象的指针表示)进行操作的 C 语言函数 [=24= .1c 以实现重入的方式实现(参见 ISO/IEC 9945:1-1996,§8.2)。
这个要求有一个缺点;由于为了重入而必须将同步构建到函数的实现中,因此它会造成很大的性能损失。 POSIX.1c 通过引入以下 C 语言标准 I/O 函数的高性能但不可重入(可能不安全)版本来解决重入(安全)和性能之间的权衡:getc() 、getchar()、putc() 和 putchar()。不可重入版本命名为getc_unlocked()等,以强调其不安全性。
但请注意,正如其他人所指出的:许多流行的系统(包括 Windows 和 Android)不 POSIX 兼容。
实际的答案似乎是流本身(应该)是线程安全的,但是,如果不是这样,您的问题可能是 fflush
发生(在锁之外)而另一个线程 正在 写入(在临界区内)。
因此,我会使用您正在编码的虚拟机模型,并将 fflush()
也放在关键部分中。
如果多个线程在同一个 FILE*
变量上同时调用 fflush()
,是否会发生任何不好的事情(如未定义行为、文件损坏等)?
澄清:我并不是说同时写入文件。我的意思是同时刷新它。
线程不会并发读取或写入文件(它们只在临界区内写入文件,一次一个线程)。他们只刷新临界区之外,尽快释放临界区,让其他人做其他工作(文件写入除外)。
尽管可能发生一个线程正在写入文件(在临界区内),而另一个线程正在刷新文件(在临界区外)。
答案很简单,您可能不会那样做,因为该文件只有一个 "current position"。你如何跟踪它?文件打开是为了顺序访问还是随机访问?在后一种情况下,您可以多次打开它(每个线程一次),然后设计保持文件结构一致的方法。
您不应该在输入流上调用 fflush()
,它会调用未定义的行为,因此我假设该流以写入或更新模式打开。
如果流以更新模式打开("w+"
或 "r+"
),调用 fflush()
时最后的操作不能是读取。由于流在各种线程中异步使用,如果没有某种形式的进程间通信和同步或锁定,如果您进行任何读取,将很难确保这一点。在更新模式下打开文件仍有正当理由,但请确保在启动 fflush
线程后不进行任何读取。
fflush()
不修改当前位置。它只会导致将任何缓冲输出写入系统。流通常受锁保护,因此在一个线程中调用 fflush()
不应弄乱另一个线程执行的输出,但它可能会改变系统写入的时间。如果多个线程输出相同的 FILE*
,交错发生的顺序无论如何都是不确定的。此外,如果您使用 fseek()
是同一流的不同线程,您 必须 使用自己的锁以确保 fseek()
和以下输出之间的一致性.
虽然看起来可以,但可能不推荐这样做。您可以在每个线程中的写操作之后,在释放锁之前调用 fflush()
。
C1 中的流是线程安全的2。在访问流之前需要函数锁定流3。
fflush 函数是线程安全的,可以随时从任何线程调用,只要该流是输出流或更新流4.
1 根据当前标准,即 C11。
2(引自:ISO/IEC 9899:201x 7.21.3 Streams 7)
每个流都有一个关联的锁,用于在多个流时防止数据竞争
执行线程访问流,并限制流操作的交错
由多个线程执行。一次只有一个线程可以持有此锁。锁是
可重入:单个线程可能在给定时间多次持有锁。
3(引自:ISO/IEC 9899:201x 7.21.3 Streams 8)
所有读取、写入、定位或查询流位置的函数都会锁定流
在访问它之前。他们在访问完成时释放与流关联的锁
完全的。
可重入:单个线程可能在给定时间多次持有锁。
4(引自:ISO/IEC 9899:201x 7.21.5.2 fflush函数2)
如果 stream 指向输出流或更新流,其中最新的
未输入操作,fflush 函数会导致该流的任何未写入数据
传送到宿主环境写入文件;否则,行为是
未定义。
POSIX.1 和对字符流(由指向 FILE 类型对象的指针表示)进行操作的 C 语言函数 [=24= .1c 以实现重入的方式实现(参见 ISO/IEC 9945:1-1996,§8.2)。
这个要求有一个缺点;由于为了重入而必须将同步构建到函数的实现中,因此它会造成很大的性能损失。 POSIX.1c 通过引入以下 C 语言标准 I/O 函数的高性能但不可重入(可能不安全)版本来解决重入(安全)和性能之间的权衡:getc() 、getchar()、putc() 和 putchar()。不可重入版本命名为getc_unlocked()等,以强调其不安全性。
但请注意,正如其他人所指出的:许多流行的系统(包括 Windows 和 Android)不 POSIX 兼容。
实际的答案似乎是流本身(应该)是线程安全的,但是,如果不是这样,您的问题可能是 fflush
发生(在锁之外)而另一个线程 正在 写入(在临界区内)。
因此,我会使用您正在编码的虚拟机模型,并将 fflush()
也放在关键部分中。