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

32位程序用临时文件实现线程通信时并发读写异常问题求助

问题根因

  • 写端文件流未强制刷入磁盘:C++标准库的fstream默认开启用户态缓冲区,加锁写入后数据仅存在进程缓冲区,未同步到内核文件系统,读线程无法感知到新写入的内容。
  • 读指针操作函数误用:tryGetData中读取数据前调用seekp设置写指针,读场景下必须使用seekg设置读指针,这是偶发读取失败的核心原因之一。
  • 流状态重置不完整:读线程触发eof后仅调用clear()清除标志位不够,需要同步当前文件的实际长度,避免读到无效的尾端数据。
  • 全局标志is_on无同步保护:普通全局变量在多线程场景下会被编译器优化为寄存器缓存,输出线程可能无法及时感知到输入线程修改的is_on状态,导致逻辑提前终止或死循环。
  • 写入顺序逻辑风险:当前先写指针表再写数据文件,极端情况下指针表写入完成后数据还未落盘,读线程拿到指针表读取数据时会读到不完整内容。

修复方案

  1. 输入线程写入后强制刷入缓冲区
    在输入线程的锁块内,写完两个流后追加flush调用:
{
    std::lock_guard lock(m);
    table_stream << t.toBinaryString();
    data_stream << tmp;
    // 新增刷入逻辑
    table_stream.flush();
    data_stream.flush();
    cout << "Written" << endl;
}
  1. 修复tryGetData的指针设置函数
    把seekp改为seekg,同时在读取前先清除流状态:
int tryGetData(std::fstream &data_stream, std::string& data, size_t data_sz, size_t offset) {
    int ret = 0;
    data = "";
    if (data_stream.is_open()) {
        // 新增清除流状态
        data_stream.clear();
        // 把seekp改为seekg
        if (offset != UINT32_MAX) data_stream.seekg(offset);

        char c;
        while (data_sz > 0 && data_stream.get(c)) {
            data.push_back(c);
            data_sz--;
        }
        if (data_stream.eof()) ret = 1;
    }
    return ret;
}
  1. 替换is_on为原子变量
    将全局的is_on改为std::atomic<bool>,避免多线程可见性问题,无需额外加锁即可保证读写的原子性和可见性。

  2. 调整写入顺序
    先写数据文件,再写指针表,保证读线程拿到指针表的时候对应数据已经落盘:

{
    std::lock_guard lock(m);
    // 先写数据,再写索引
    data_stream << tmp;
    table_stream << t.toBinaryString();
    data_stream.flush();
    table_stream.flush();
    cout << "Written" << endl;
}

如果需要更高性能,可以调整缓冲区大小,或者使用操作系统级的内存映射文件代替直接fstream读写,性能会比逐字符get高很多。

内容的提问来源于stack exchange,提问作者Ales100

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 08:45:01