读写同一文件是否线程安全?

8

我有一个名为"data.txt"的文件。

我有两个线程。

第一个线程读取整个文件的内容:

while(1){
    char buf[1000];
    FILE* fp = fopen("data.txt","r");
    while(fread(buf,1,1000,fp)>0){
        /* process data */
    }
    fclose(fp);
}

第二个线程向文件中追加数据:
while(1){
    FILE* fp = fopen("data.txt","a");
    fwrite("hello\n",1,6,fp);
    fclose(fp);
}

在没有任何MUTEX或FILELOCKING的情况下,读写是否是线程安全的?(没有分段错误等问题)

我可能错了,但我认为POSIX仅指定操作系统接口,而不是C库。因此,如果fwrite和fread是线程安全的,那么POSIX并不知道。它确实指定了read()和write()是线程安全的。 - Zan Lynx
我怀疑你不会崩溃,但我也怀疑你的读取线程从文件中读取的数据是否特别明确定义。特别是,如果你希望该文件的行为类似于FIFO管道(其中线程2写入的每个字节都会按顺序被线程1读取),那么你可能会感到失望。 - Jeremy Friesner
你试过了吗?应该很容易证明它不安全。 - Dwayne Towell
感谢您的回复。在Windows和Fedora上,根本没有分段错误。因此我认为它是线程安全的... - user3412796
3
没有分段错误不意味着线程安全。 - jfly
@Zan Lynx:POSIX确实定义了C标准库,例如:http://pubs.opengroup.org/onlinepubs/009695299/functions/fread.html - Peter - Reinstate Monica
2个回答

7
首先,大多数标准库函数(除显式解锁的I/O函数外)都是官方线程安全的。详见http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html。其中http://pubs.opengroup.org/onlinepubs/009695299/functions/flockfile.html明确说明了flockfile()只有在特殊要求时才需要使用。
关于不安全的“未解锁”函数,需要注意的是:
“只有当调用线程拥有(FILE*)对象并且已成功调用flockfile()或ftrylockfile()函数后,这些函数才可在多线程程序中安全地使用。”
这意味着正常的、“锁定”的函数的线程安全保证比您当前的操作更强:甚至在使用相同的FILE指针(即一个fopen()的结果)时也很安全。可以想象,在FILE结构体中同时更新簿记信息可能会导致文件损坏,但正常的标准库函数保证不会出现这种情况。
另一方面,C标准说:“是否可以同时多次打开同一文件也是实现定义的。” 有CERT建议避免这种情况。您的用例是使用两个fopen()调用获取多个FILE结构体,潜在地没有使用fclose()将它们连接到同一个底层物理文件上。
标准将此视为实现定义,可能反映了某些操作系统的(潜在)限制。
另外需要注意的一点是:成功尝试并发算法几次并不意味着它是正确的。并发问题是可怕的野兽,会以不可预测的方式出现。

2

fread()fwrite()在本质上不是线程安全的。它们不太可能导致程序崩溃,但读取和写入的顺序不能保证。

如果您打算同时使用它们,则必须使用flockfile()funlockfile()


非常感谢。请问您能回答我的另一个相关问题吗: http://stackoverflow.com/questions/22367231/is-reading-and-writing-to-the-same-file-at-different-bytes-range-thread-safe - user3412796
确定吗?请参考我帖子中的链接。 - Peter - Reinstate Monica
3
它们是线程安全的。线程安全并不意味着操作的顺序。 - William Pursell
你把线程安全(例如使用互斥锁)和同步(例如使用监视器)混淆了:https://en.wikipedia.org/wiki/Monitor_(synchronization) - pooya13

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