这个文件操作线程安全吗?

时间:2013-02-13 01:31:11

标签: c linux multithreading

我想知道我的PRINT宏的线程安全性并编写了一个程序来查看我的情况下的文件操作是否是线程安全的,所以我在fn0中添加了sleep并发现它是线程安全的。

$cat t
fn1
fn0

打印并且不会覆盖。

测试是否足够好或是否存在其他情况 PS:我没有共享文件指针 我对订购很满意(无序/非顺序很好)我只对腐败/覆盖感兴趣 - 似乎文件指针移动得很好。

-----------宏-------------

#define PRINT(args ...) if (logflag) { \
FILE *flog = fopen(LOGFILE, "a"); \
fprintf( flog, args); \
fclose(flog); \
}  fprintf(stderr, args); fflush(stderr);

-------------------测试编程-------------------------- -

#include <stdio.h>
#include <pthread.h>

#define LOGFILE "t"
char c='1';
void *fn0(void* v)
{

    FILE *flog = fopen(LOGFILE, "a");
    //sleep(2);
    fprintf( flog,"%s\n", "fn0");
    fclose(flog);

    printf ("Enter value ");
    c=getchar();
}

void* fn1(void*v)
{
    FILE *flog = fopen(LOGFILE, "a");
    fprintf( flog,"%s\n", "fn1");
    fclose(flog);
}
int main()
{
    pthread_t t0;
    pthread_t t1;


    pthread_create(&t0, NULL, fn0, (void *) NULL);
    pthread_create(&t1, NULL, fn1, (void *) NULL);
    pthread_join(t1, NULL);
    pthread_join(t0, NULL);

    return 0;
}

2 个答案:

答案 0 :(得分:3)

您的代码不是线程安全的。如果其中一个线程尝试打开该文件而另一个线程打开该文件,则第二次打开尝试可能会因共享冲突而失败。即使操作系统允许您同时打开文件两次,您现在还有两个独立的非同步缓冲区和两个不同的文件指针。绝对不能保证你的写作是一致的。

您应该使用互斥锁序列化对共享资源的访问。

或者,使用生产者/消费者方法并将所有写入日志放在单个线程上,即消费者。然后让多个生产者线程将日志记录任务推送到消费者线程上。

答案 1 :(得分:2)

该代码非常接近线程安全。它确实是线程安全的,因为它不会:

  • 破坏了进程内存
  • 由于同时访问内存数据结构而导致段错误。
  • 导致读取部分写入的数据(例如在一个线程中填充缓冲区并同时读取另一个线程)

然而,正如David Heffeman所提到的,它在写入文件的层面上并不是线程安全的。虽然很接近。使用系统调用来代替缓冲IO,例如使用open + write而不是fopen + fwrite,可以用来获取原子写入和线程安全。

有关确保写入是原子的更多详细信息,请参阅此答案: Is file append atomic in UNIX?

另一件事;全局char c的使用是有风险的,尽管在所示的上下文中使用它是线程安全的。如果在函数c中声明fn0,则会消除潜在问题(只要它保留为自动变量且未声明为static)。

关于测试这类案例的主题 - 根据我的经验,压力测试可以提供可靠的结果,但需要一些努力来进行压力测试 - 而压力测试通常会带来各种细微差别,这些很好修复,但可能是压倒性的和沮丧的。为了进行压力测试,只需设置一个程序,该程序在可配置的迭代次数上旋转可配置数量的线程。对于像这样的测试,我建议从20个线程和1000个迭代开始。

话虽如此,我同意线程安全和竞争条件永远不会被100%证明不会仅通过测试来存在。