Linux下的文件操作,有人喜欢用C库的文件流操作,有人喜欢用Linux的原生系统调用。一般来说,C库的文件操作会更高效,因为C库自己做文件缓存。今天主要学习fwrite和多线程下的写。每个线程写入相同的FILE*或fd,以查看结果是否是预期的行为。
第一种情况:fwrite使用C库,其线程实现如下:
在第二种情况下,系统调用write,线程实现如下:
让我们来看看主线程的实现:
其中循环被定义为1000000。也就是说,线程1到3写& # 34;aaaaaa \ n & # 34、“bbbbbb\n”和& # 34;cccccc \ n & # 34每个一百万次。如果写文件的操作是线程安全的,那么最终的文件行数应该是300万,每行只能& # 34;aaaaaa & # 34,“bbbbbb”,还有& # 34;cccccc & # 34一种。
【文章福利】需要C/C++ Linux服务器架构师学习资料加裙子812855908(资料包括C/C++、Linux、golang技术、Nginx、ZeroMQ、MySQL、Redis、fastdfs、MongoDB、ZK、流媒体、CDN、P2P、K8S、Docker、TCP/IP、Concordance。
接下来,看看测试结果:
1.定义宏USE_CLIB,即使用C库的fwrite,结果如下:
2.注释掉红色的USE_CLIB,也就是直接用系统调用write,结果如下:
从上面的测试结果来看,无论是C库的fwrite还是系统调用的write都可以保证输出不会混合——也就是多线程的输出不会混合,但是在使用系统调用的write时,最终的文件行数出乎意料,远远小于300万行的总数。这也证明了写系统调用不是线程安全的。在多线程下,它们的输出会互相覆盖。C库的fwrite是一个线程安全的函数。
为什么结果会这样?让我们先来看看fwrite的实现:
在fwrite内部,它使用锁来确保操作的序列化,从而实现线程安全。
write的实现如下图所示:
使用file_pos_read在写入前获取偏移量。如果在多核多线程的情况下,两个内核可能同时陷入内核状态,同时获得文件的当前偏移量,那么两个值必须相等。因此两个线程将数据写入相同的偏移量。最后,文件的实际大小不是预期的大小。
最后得出的结论是:
C库的fwrite是一个线程安全的函数,系统调用write用一个额外的标志位O_APPEND进行额外的写入,以保证偏移量不重叠,实现预期的并发写入——你可以通过修改下面的测试代码,在自己的环境中进行测试。
#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <pthread.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>//#define USE_CLIB#define TEST_FILE"./tmp.txt"#define LOOPS(1000000)#ifdef USE_CLIBstruct thr_data {FILE *fp;const char *data;};static void * write_data(void *data){struct thr_data *d;size_t len;int i;d = data;len = strlen(d->data);for (i = 0; i < LOOPS; ++i) {fwrite(d->data, len, 1, d->fp);}return NULL;}#elsestruct thr_data {int fd;const char *data;};static void *write_data(void *data){struct thr_data *d;int i;size_t len;d = data;len = strlen(d->data);for (i = 0; i < LOOPS; ++i) {write(d->fd, d->data, len); }return NULL;}#endifint main(void){pthread_t t1, t2, t3;struct thr_data d1, d2, d3;#ifdef USE_CLIBFILE *fp = fopen(TEST_FILE, "w");d1.fp = d2.fp = d3.fp = fp;#else//int fd = open(TEST_FILE, O_WRONLY|O_TRUNC);int fd = open(TEST_FILE, O_WRONLY|O_TRUNC|O_APPEND);d1.fd = d2.fd = d3.fd = fd;#endifd1.data = "aaaaaa\n";d2.data = "bbbbbb\n";d3.data = "cccccc\n";pthread_create(&t1, NULL, write_data, &d1);pthread_create(&t2, NULL, write_data, &d2);pthread_create(&t3, NULL, write_data, &d3);pthread_join(t1, NULL);pthread_join(t2, NULL);pthread_join(t3, NULL);#ifdef USE_CLIBfclose(fp);#elseclose(fd);#endifreturn 0;}
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。