1

我想知道我的 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;
}
4

2 回答 2

3

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

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

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

于 2013-02-13T01:33:40.010 回答
2

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

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

但是,正如 David Heffeman 所提到的,在写入文件的级别上它不是线程安全的。虽然很接近。使用系统调用写入而不是缓冲 IO,例如使用 open + write 而不是 fopen + fwrite,可用于获得原子写入和线程安全。

有关确保写入是原子的更多详细信息,请参阅此答案: 文件附加在 UNIX 中是原子的吗?

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

关于像这样的测试用例——根据我的经验,压力测试可以给出可靠的结果,但压力测试需要付出一些努力——压力测试通常会带来各种细微差别,这些细微差别很好解决,但可以压倒性的和令人沮丧的。为了进行压力测试,只需设置一个程序,该程序在可配置数量的迭代中衍生出可配置数量的线程。对于这样的测试,我建议从 20 个线程和 1000 次迭代开始。

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

于 2013-08-26T20:12:07.243 回答