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

为何该C++多线程程序触发核心转储?是否存在死锁?

C++线程安全演示程序核心转储及无输出问题分析

我编写了一款用于演示线程安全与非线程安全的C++程序,若运行正常则无问题。但实际运行时出现核心转储,且标注为safe的分支无输出。现咨询:该程序为何会触发核心转储?是否存在死锁?

相关代码如下:

#include <iostream>
#include <thread>
#include<vector>
#include <mutex>

using namespace std;

class Balance {
public:
    int num;

    explicit Balance(int num) : num(num) {};
    std::mutex m;
};


void transfer_unsafe(Balance &from, Balance &to, int n) {
    from.num -= n;
    to.num += n;
}


void transfer(Balance &from, Balance &to, int n) {
    std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);
    std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);
    std::lock(from.m, to.m);
    from.num -= n;
    to.num += n;
}


int main() {


    auto test = [](const string &name, const auto func) {

        auto start_time = std::chrono::high_resolution_clock::now();
        Balance a{100};
        Balance b{100};
        std::cout << name << std::endl;
        std::cout << "before " << a.num << " " << b.num << std::endl;

        std::vector<std::thread> threads;

        threads.reserve(100);
        for (int i = 0; i < 1000; i++) {
            threads.push_back(std::thread{func, std::ref(a), std::ref(b), 1});
        }

        for (int i = 0; i < 1000; i++) {
            threads.push_back(std::thread{func, std::ref(b), std::ref(a), 1});
        }

        for (auto &t: threads) {
            t.join();
        }
        std::cout << "after " << a.num << " " << b.num << std::endl;
        auto end_time = std::chrono::high_resolution_clock::now();
        std::cout << "time : " << (end_time - start_time).count() << std::endl;
    };

    test("unsafe", transfer_unsafe);
    test("safe", transfer);


}

核心转储原因

  • 非线程安全分支的数据竞争:transfer_unsafe函数中,多个线程无同步地读写Balance的num成员,属于C++标准中的未定义行为。这种情况下,多个线程同时操作同一内存地址可能导致指令重排、部分写操作等内存混乱问题,直接触发核心转储。
  • 线程容器预留空间不足:threads.reserve(100)仅预留100个线程的空间,但后续创建了2000个线程。虽然std::thread支持移动语义,但vector频繁扩容可能引发不可预测的内存操作问题,加剧核心转储风险。

是否存在死锁?

是的,safe分支存在明确死锁,这也是该分支无输出的核心原因:
在transfer函数中,错误地直接调用std::lock(from.m, to.m),而非传入unique_lock对象管理锁:

  1. lock1和lock2以defer_lock模式创建,并未持有锁;
  2. std::lock(from.m, to.m)直接锁住原始互斥量,但未通过unique_lock接管锁的生命周期;
  3. 函数结束时,lock1和lock2销毁不会解锁互斥量,导致两个互斥量永久锁定。

后续所有尝试获取这两个互斥量的线程都会被阻塞,程序卡在join步骤无法继续执行,自然无法输出后续内容,甚至因资源耗尽触发核心转储。


修复方案

  1. 修正safe分支的锁管理逻辑:将std::lock(from.m, to.m)改为传入unique_lock对象,让其自动管理锁的解锁:
void transfer(Balance &from, Balance &to, int n) {
    std::unique_lock<std::mutex> lock1(from.m, std::defer_lock);
    std::unique_lock<std::mutex> lock2(to.m, std::defer_lock);
    std::lock(lock1, lock2); // 由unique_lock接管锁的生命周期
    from.num -= n;
    to.num += n;
}
  1. 调整线程容器预留空间:匹配实际创建的线程数量,避免频繁扩容:
threads.reserve(2000);
  1. 可选:同步输出操作:添加全局互斥量避免多线程输出混乱:
std::mutex cout_mutex;

// 在输出代码处添加锁:
std::lock_guard<std::mutex> lock(cout_mutex);
std::cout << name << std::endl;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 10:22:31