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

两个线程操作std::deque,下述代码是否存在竞争条件?

为什么std::deque在多线程操作下会出现元素重复或丢失?

你遇到的问题本质是std::deque不是线程安全容器,所有成员函数都不支持并发读写操作,哪怕是看似“只读”的empty()或front(),在有其他线程修改容器时调用都会触发未定义行为。

具体竞争场景分析

你的代码里存在多个线程安全问题:

  1. 容器操作的竞态:当push线程执行push_back()时,deque可能正在修改内部的存储结构(比如扩容、调整缓冲区指针),此时pop线程调用empty()、front()或pop_front(),会读取到不一致的内部状态,导致:
    • 读取front()时拿到已经被弹出的元素(重复输出)
    • pop_front()操作错误地跳过元素(丢失)
    • 甚至可能直接导致程序崩溃
  2. 全局变量的无保护访问:num、m、n、stop这些全局变量的读写都是非原子操作,比如m++实际包含“读-改-写”三步,多线程下会出现计数错误,进一步加剧行为异常。
  3. 检查-执行的窗口漏洞:哪怕是单pop线程,if (!q.empty())和后续的q.front()/q.pop_front()之间不是原子操作,在这两个步骤之间,push线程可能还没完成push_back()的全部操作,或者极端情况下(比如push线程刚好在此时完成最后一个push并设置stop),都会导致逻辑错误。

平台差异的原因

MacOS使用的是libc实现的STL,Linux常用的是libstdc,两者对deque的内部实现(比如缓冲区管理、扩容时机)不同,libc++的实现可能更容易在这种无保护的并发场景下暴露竞态问题,所以你在Mac上更容易复现异常,而Linux上只是难复现,不是没有问题。

修复方案

要解决这个问题,必须给所有共享资源的访问加锁保护,示例修复代码如下:

#include <mutex>
#include <condition_variable>
#include <deque>
#include <thread>
#include <iostream>
#include <cstdio>

std::mutex q_mutex;
std::condition_variable q_cv;
int num = 10000;
int m = 0;
int n = 0;
bool stop = false;
std::deque<int> q;

void push()
{
    while(num--) {
        std::lock_guard<std::mutex> lock(q_mutex);
        q.push_back(m);
        printf("push succ: %d\n", m);
        m++;
    }
    {
        std::lock_guard<std::mutex> lock(q_mutex);
        stop = true;
    }
    q_cv.notify_one(); // 通知pop线程检查结束条件
    std::cout << "m: " << m << std::endl;
}

void pop()
{
    while(true) {
        std::unique_lock<std::mutex> lock(q_mutex);
        // 等待队列非空或收到停止信号
        q_cv.wait(lock, []{ return !q.empty() || stop; });
        
        if (stop && q.empty()) {
            break;
        }
        
        if (!q.empty()) {
            printf("front: %d\n", q.front());
            q.pop_front();
            n++;
            printf("n: %d\n", n);
        }
        lock.unlock();
        std::this_thread::sleep_for(std::chrono::milliseconds(4));
    }
    std::cout << "final n: " << n << std::endl;
}

关键修复点:

  • 用std::mutex保护所有对q、m、n、stop、num的访问,确保同一时间只有一个线程操作这些共享资源
  • 用std::condition_variable替代空轮询,避免CPU空耗的同时,消除“检查-执行”的竞态窗口
  • 所有涉及共享资源的操作都被严格限制在锁的保护范围内

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 13:23:25