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

多线程共享std::list日志队列时,读取pop操作是否需要加互斥锁?

结论

必须在popLog方法中加锁,否则必然存在线程安全隐患,和日志写入频率无关,低频率下未出问题只是没触发竞争场景,不代表逻辑正确。

问题原因

std::list本身不是线程安全容器,你的场景下存在多个线程并发修改同一个容器的行为:

  • 写入线程调用push_back会修改链表的尾节点指针、内部维护的长度计数等核心数据
  • UI线程调用size()/front()/pop_front会读取长度计数、修改头节点指针,同样属于容器修改操作
    并发操作没有同步的情况下,会直接触发未定义行为,常见表现包括:日志内容乱码、程序直接崩溃、日志莫名丢失、明明有日志却读到空字符串等,写入频率越高,触发问题的概率越大。

修复后的代码

std::string popLog() {
    std::lock_guard<std::mutex> lk(m_mutex);
    // 推荐用empty()替代size() == 0,部分STL实现中empty()是O(1)且更高效
    if (m_queLogs.empty()) {
        return "";
    }
    // 用std::move避免字符串拷贝,提升性能
    auto zLog = std::move(m_queLogs.front());
    m_queLogs.pop_front();
    return zLog;
}

可选优化(减少锁竞争)

你可以改成单次加锁批量读取所有日志,减少UI线程加锁的频次,进一步降低对高频率写入线程的影响:

// Logger新增批量读取接口
std::vector<std::string> popAllLogs() {
    std::lock_guard<std::mutex> lk(m_mutex);
    std::vector<std::string> logs;
    logs.reserve(m_queLogs.size());
    while (!m_queLogs.empty()) {
        logs.push_back(std::move(m_queLogs.front()));
        m_queLogs.pop_front();
    }
    return logs;
}

// UI线程修改为批量追加
void CLogView::OnTimer(UINT_PTR nIDEvent)
{
    auto logs = Logger::instance()->popAllLogs();
    for (const auto& zLog : logs) {
        CString strLog = convert_utf8_to_cstring(zLog);
        m_ctrLogView.AppendString(strLog);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 03:12:02