多线程向同一文件写入相同值是否具备线程安全性?
多线程文件写入的线程安全性分析
问题描述
我有一个单进程多线程的C程序,线程会向文件写入相同的值(比如"0"),期望最终文件仅包含一个"0"而非多个。当前测试用例看似通过,但需要100%确认其线程安全性。
现有代码
#include <fcntl.h> #include <string.h> #include <unistd.h> #include <pthread.h> void *ThreadFunction(void *ptr) { for (int i=0; i<10; ++i) { const int Concurrent_fd = open("/dev/ConcurrencyTest.txt", O_RDWR); char* Value = "0"; write(Concurrent_fd, Value, strlen(Value)); close(Concurrent_fd); } return NULL; } int main() { static const int NbThreads = 50; pthread_t Threads[NbThreads]; for(int i=0; i<NbThreads; ++i) { pthread_create(&Threads[i], NULL, ThreadFunction, NULL); } for(int i=0; i<NbThreads; ++i) { pthread_join(Threads[i], NULL); } }
编译命令
gcc -pthread -o program-output Test.c
线程安全性分析
现有代码完全不具备线程安全性,无法保证文件最终仅含一个"0",原因如下:
- 每个线程独立打开文件,各自持有独立的文件描述符,每个描述符维护自己的文件偏移量。默认情况下,
O_RDWR打开已存在文件时,偏移量初始化为0,但每次write后偏移量会自动后移。 - 多线程并发执行
open、write、close操作时,没有任何同步机制:- 线程A打开文件,偏移量0,写入"0"后偏移量变为1;
- 同时线程B打开文件,偏移量0,写入"0"后偏移量变为1;
- 最终文件会累积所有线程的写入操作,出现大量"0"(理论上最多50*10=500个)。
- 测试用例"通过"大概率是巧合,比如测试时线程执行顺序刚好让最后一次写入覆盖了之前的内容,但这不是必然结果。
正确的线程安全实现方案
要保证最终文件仅含一个"0",需要通过同步机制确保同一时间只有一个线程对文件进行操作,或每次写入时覆盖整个文件。以下是两种可靠方案:
方案1:使用线程互斥锁(推荐)
通过全局互斥锁,让所有线程的文件操作串行执行,彻底消除竞争:
#include <fcntl.h> #include <string.h> #include <unistd.h> #include <pthread.h> // 全局互斥锁,所有线程共享 pthread_mutex_t file_mutex = PTHREAD_MUTEX_INITIALIZER; void *ThreadFunction(void *ptr) { for (int i=0; i<10; ++i) { // 加锁,确保同一时间只有一个线程进入临界区 pthread_mutex_lock(&file_mutex); const int Concurrent_fd = open("/dev/ConcurrencyTest.txt", O_WRONLY | O_TRUNC); if (Concurrent_fd != -1) { char* Value = "0"; write(Concurrent_fd, Value, strlen(Value)); close(Concurrent_fd); } // 解锁,允许其他线程进入 pthread_mutex_unlock(&file_mutex); } return NULL; } int main() { static const int NbThreads = 50; pthread_t Threads[NbThreads]; for(int i=0; i<NbThreads; ++i) { pthread_create(&Threads[i], NULL, ThreadFunction, NULL); } for(int i=0; i<NbThreads; ++i) { pthread_join(Threads[i], NULL); } // 销毁互斥锁 pthread_mutex_destroy(&file_mutex); return 0; }
- 新增
O_TRUNC标志,打开文件时直接清空内容,确保每次写入都是覆盖整个文件; - 互斥锁保证文件操作的原子性,无论线程如何并发,最终文件只会保留最后一次写入的"0"。
方案2:使用文件锁(适用于跨进程场景)
如果未来需要扩展到多进程场景,可以用fcntl实现文件锁,替代线程互斥锁:
#include <fcntl.h> #include <string.h> #include <unistd.h> #include <pthread.h> void *ThreadFunction(void *ptr) { struct flock lock; lock.l_type = F_WRLCK; // 写锁 lock.l_whence = SEEK_SET; // 从文件开头开始 lock.l_start = 0; lock.l_len = 0; // 锁定整个文件 for (int i=0; i<10; ++i) { const int Concurrent_fd = open("/dev/ConcurrencyTest.txt", O_WRONLY | O_TRUNC); if (Concurrent_fd == -1) continue; // 获取文件锁,阻塞等待直到拿到锁 fcntl(Concurrent_fd, F_SETLKW, &lock); char* Value = "0"; write(Concurrent_fd, Value, strlen(Value)); // 释放文件锁 lock.l_type = F_UNLCK; fcntl(Concurrent_fd, F_SETLK, &lock); close(Concurrent_fd); } return NULL; } // main函数同原代码...
内容的提问来源于stack exchange,提问作者TheChamp
相关产品推荐
相关产品推荐

