You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程向同一文件写入相同值是否具备线程安全性?

多线程文件写入的线程安全性分析

问题描述

我有一个单进程多线程的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操作时,没有任何同步机制:
    1. 线程A打开文件,偏移量0,写入"0"后偏移量变为1;
    2. 同时线程B打开文件,偏移量0,写入"0"后偏移量变为1;
    3. 最终文件会累积所有线程的写入操作,出现大量"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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 03:02:42