fclose()、fprintf()、ftell()线程仅就每个函数本身而言是安全的吗?
Glibc说fclose()/fopen()/fprintf()/ftell()是线程安全的。但是,当一个线程正在写入或读取文件,而另一个线程正在关闭文件时,会发生什么情况呢 假设我有一个这样的函数fclose()、fprintf()、ftell()线程仅就每个函数本身而言是安全的吗?,c,linux,C,Linux,Glibc说fclose()/fopen()/fprintf()/ftell()是线程安全的。但是,当一个线程正在写入或读取文件,而另一个线程正在关闭文件时,会发生什么情况呢 假设我有一个这样的函数 FILE * f; //f is opened when program starts int log(char * str) { fprintf(f, "%s", str); if (ftell(f) > SIZE_LIMIT) { pthread_mutex_loc
FILE * f; //f is opened when program starts
int log(char * str)
{
fprintf(f, "%s", str);
if (ftell(f) > SIZE_LIMIT) {
pthread_mutex_lock(&mutex);
if (ftell(f) > SIZE_LIMIT) {
fclose(f);
rename(OLD_PATH, NEW_PATH);
f = open(OLD_PATH, "a");
}
pthread_mutex_unlock(&mutex);
}
}
多个线程使用此函数写入文件。是否安全,即无碰撞?请注意,函数返回错误很好,我的实验表明程序间歇性崩溃
编辑:
1.正如@2501所指出的,“关联文件关闭后,指向文件对象的指针的值是不确定的”,这解释了间歇性崩溃的原因。
如果我用freopen重写代码呢
pthread_mutex_lock(&mutex);
if (ftell(f) > SIZE_LIMIT) {
rename(OLD_PATH, NEW_PATH);
f = freopen(OLD_PATH, "a", f);
}
pthread_mutex_unlock(&mutex);
每个函数都会锁定与
文件*
关联的互斥锁。因此,对于特定的文件*
对象,这些函数是“原子的”。但是一旦文件*
对象关闭,它就不能使用了。因此,如果文件*
关闭,而另一个线程尝试使用该关闭的文件*
,那么您将由于尝试写入关闭的文件而失败
请注意,这不包括在不与其他线程同步的情况下更改
f
变量时可能发生的任何数据争用。(从我们看到的代码片段中,不清楚是否存在种族,但我猜可能存在)。使用fclose关闭流后,文件
指针的值不确定。这意味着使用它会导致未定义的行为
7.21.3文件
f
的值不确定时,行为或代码未定义
要定义代码,fprintf调用和使用指针的任何其他调用也应被互斥锁锁定。关注安全的代码应检查各种I/O函数的结果。(可能是为了简化而将它们放在这里。)即使使用线程安全函数,您也不能在另一个线程正在或可能正在使用资源时释放资源。如果您担心性能,您可以使用
fputs(str,fp)代码>而不是fprintf(fp,“%s”,str)
@AndrewHenle谢谢,性能差异是由于fprintf()中的格式处理开销造成的吗?@wei是由于fprintf()中的格式处理开销造成的性能差异吗?是的。根据实施情况,它可能是实质性的。例如,前1200行左右都是在fprintf()
处理中使用的宏(GLIBC使用vfprintf()
实现fprintf()
)(请注意,在该实现中,仅传递一个简单字符串会绕过所有处理过程-不要使用该方法,因为如果有人将包含%
的字符串传递给您的log()
调用,则会导致问题。)啊,这个不确定值解释了我看到的崩溃。如果使用freopen()就像我在orig post中编辑的一样?@wei如果我像在orig post中编辑的一样使用freopen(),那会怎么样?这可能会更糟,具体取决于实现情况。我看不到任何保证要求freopen()
始终保持内部一致的文件*
对象。即使它是静音的,隐含的fclose()< /代码>可以在调用<代码> fPrtff()中的任何线程下使对象无效。
。它可能会工作,但即使它工作了,这样做最好也取决于实现,最坏情况下是未定义的。@因为freopen首先关闭与流关联的文件,在打开它之前,行为仍然未定义。@AndrewHenle&2501谢谢,我想我必须在fprintf/fputs()之前使用rdwr锁@您应该能够使用flockfile()
和funlockfile()
并包装整个日志()而不是单独的互斥锁或其他锁
函数。有关说明和示例,请参阅和:注意,如果您在Linux或Solaris上运行,可以将流设置为绕过文件*
对象的内部锁定,从而使整个日志()
调用原子只有一个锁定/解锁周期。我得到的印象是,写入一个关闭的文件流会返回错误,但我没有意识到“f”可能指向无效的内容。如果我像我在orig post中编辑的那样使用freopen()会怎么样?如果freopen()
由于某些原因返回一个错误。否则,我认为它可能没问题。但我真的不是100%确定。谢谢。我看了一下glibc的freopen()实现,它不会释放文件数据,因此它始终保持文件锁,不像fclose()那样释放锁然后释放文件结构。但我想这可能是实现依赖的,正如Andrew在另一个答案中所说。