C++多线程:4种典型方案深度解析

🚀C++多线程编程实战指南:4种典型方案深度解析


还在为多线程数据竞争和死锁问题头疼?本文通过4个真实代码示例,深度剖析:

  • 🔥 原子操作的无锁编程技巧
  • 🔒 互斥锁的最佳使用姿势
  • Qt线程方案的工程实践
  • 🚀 moveToThread的高效应用场景
icon


📑 1 实现方案

1️⃣ 使用原子控制保存数据

示例代码:


class Data : public std::enable_shared_from_this<Data> {
public:
    Data() { startWorkerThread(); }
    ~Data() { stopWorkerThread(); }
private:
    class Worker;
    std::unique_ptr<Worker> m_worker;
    std::atomic<int> data{0};

    void startWorkerThread() {
        if(m_worker) 
            return;
        m_worker = std::make_unique<Worker>(shared_from_this());

        // 启动线程
        m_worker->start();
    }
    void stopWorkerThread() {
        if (m_worker) {
            m_worker->stop();
            m_worker.reset();
        }
    }
};

class Data::Worker {
public:
    Worker(std::shared_ptr<Data> data){
        m_data = data;
    }
    ~Woker(){}

    void start(){
        m_stop = false;
        if(m_thread.joinable()) {
            m_thread.join();
        }
        m_thread = std::thread([this] { run(); });
    }
    void stop(){
        m_stop = true;
        if(m_thread.joinable()) {
            m_thread.join();
        }
    }
    
private:
    std::atomic<bool> m_stop{false};
    std::thread m_thread;
    std::weak_ptr <Data> m_data; // 使用弱指针,避免循环引用

    void run() {
        while(!m_stop){
            auto data_ptr = m_data.lock();
            if(!data_ptr) {
                std::this_thread::sleep_for(std::chrono::milliseconds(50));
                continue;
            }            
            int data = m_data->data.load(std::memory_order_acquire);
            if (data > 0) {
                auto temp = data * 3 / 2;
                m_data->data.store(temp, std::memory_order_release);
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(2));
        }
    }
};

// 用户必须通过 shared_ptr 创建 Data 对象
auto data = std::make_shared<Data>();

2️⃣ 使用互斥锁保护数据

示例代码:

#include <mutex>
#include <thread>

class Data {
public:
    void decrement() {
        std::lock_guard<std::mutex> lock(m_mutex);
        if (data > 0) data--;
    }

    int get() const {
        std::lock_guard<std::mutex> lock(m_mutex);
        return data;
    }

private:
    mutable std::mutex m_mutex;
    int data = 0;
};

class Worker {
public:
    explicit Worker(std::shared_ptr<Data> data) : m_data(data), m_stop(false) {}

    void start() {
        m_thread = std::thread([this] {
            while (!m_stop.load(std::memory_order_acquire)) {
                m_data.decrement();
                std::this_thread::sleep_for(std::chrono::milliseconds(2));
            }
        });
    }

    void stop() {
        m_stop.store(true, std::memory_order_release);
        if (m_thread.joinable()) m_thread.join();
    }

private:
    std::weak_ptr<Data> m_data; // 使用弱指针,避免循环引用
    std::atomic<bool> m_stop;
    std::thread m_thread;
};

int main(){
    std::shared_ptr<Data> data = std::make_shared<Data>();
    Worker worker(data);
    worker.start();

    std::this_thread::sleep_for(std::chrono::seconds(5));
    worker.stop();
    return 0;
}

3️⃣ 使用QThread

示例代码:

#include <QAtomicInteger>

class Data {
public:
    Data() : m_total(0), m_count(0) {}

    void calculate(int value) {
        m_total.fetchAndAddOrdered(value);  // 原子加法
        m_count.fetchAndAddOrdered(1);      // 原子递增
    }

    int getAverage() const {
        const int count = m_count.loadAcquire();
        const int total = m_total.loadAcquire();
        return (count == 0) ? 0 : total / count;
    }

private:
    QAtomicInt m_total;  // 总和
    QAtomicInt m_count;  // 计数
};

class Worker : public QThread {
    Q_OBJECT
public:
    // 通过智能指针管理Data所有权
    explicit Worker(QPointer<Data> data, QObject* parent = nullptr)
        : QThread(parent), m_data(data), m_stop(false) {}

    void stop() {
        m_stop = true;
        if (isRunning()) {
            quit();
            wait();  // 等待线程结束
        }
    }

signals:
    void averageUpdated(int value);

protected:
    void run() override {
        QRandomGenerator rand(QTime::currentTime().msec());
        while (!m_stop) {
            // 检查Data指针有效性
            if (m_data.isNull()) break;

            // 更新数据并通知
            const int num = rand.bounded(1, 100);
            m_data->calculate(num);
            emit averageUpdated(m_data->getAverage());

            QThread::msleep(100);
        }
    }

private:
    QPointer<Data> m_data;    // 共享数据指针
    QAtomicInteger<bool> m_stop{false};
};

int main(){
    QPointer<Data> data = new Data();
    // 启动工作线程
    Worker worker(data);
    QObject::connect(&worker, &Worker::averageUpdated, [](int avg) { qDebug() << "Current average:" << avg; });
    worker.start();

    // 运行5秒后停止
    QTimer::singleShot(5000, [&worker] {
        worker.stop();
    });

    return 0;
}

4️⃣ 使用moveToThread

示例代码:

class Data {
public:
    void calculate(int value) {
        QMutexLocker locker(&m_mutex);
        m_total += value;
        m_count++;
    }

    int getAverage() const {
        QMutexLocker locker(&m_mutex);
        return (m_count == 0) ? 0 : m_total / m_count;
    }

private:
    mutable QMutex m_mutex;  // 允许const方法加锁
    int m_total = 0;
    int m_count = 0;
};

class Worker : public QObject {
    Q_OBJECT
public:
    explicit Worker(QPointer<Data> data, QObject* parent = nullptr) : QObject(parent), m_data(data) {}

    void start() {
        QRandomGenerator rand;
        while (!m_stop) {
            // 检查指针有效性
            if (!m_data) break;

            // 更新数据并通知
            const int num = rand.bounded(1, 100);
            m_data->calculate(num);
            emit averageUpdated(m_data->getAverage());

            QThread::msleep(100);
        }
        emit workFinished();
    }

    void stop() {
        m_stop = true;
    }

signals:
    void averageUpdated(int value);
    void workFinished();

private:
    QPointer<Data> m_data;
    std::atomic<bool> m_stop{false};
};

int main(){
    QThread thread = new QThread();
    QPointer<Data> data = QPointer<Data>(new Data());
    Worker worker(data);
    worker.moveToThread(thread);

    QObject::connect(thread, &QThread::finished, [thread] {
        thread->quit();
        thread->deleteLater();
    });
    QObject::connect(thread, &QThread::started, [worker] {
        worker.start();
    });

    QObject::connect(&worker, &Worker::averageUpdated, [](int avg) { qDebug() << "Current average:" << avg; });
    QObject::connect(&worker, &Worker::workFinished, [thread] { thread->quit(); });
    
    // 启动线程
    thread->start();

    // 运行5秒后停止
    QTimer::singleShot(5000, [&worker] {
        worker.stop();
        worker.quit();
        worker.wait();
    });

    return 0;
};

🔎 2 方案对比

1. 原子控制保存数据(无锁编程)

优点

  • 性能高效:原子操作无锁,适用于高频读写场景
  • 轻量级:适合简单数据类型(int/bool等)
  • 内存序控制:可通过memory_order精细优化

缺点

  • 适用性有限:仅适用于简单数据类型
  • 逻辑复杂度高:需要严格设计内存访问顺序
  • 容易出错:自旋等待可能消耗CPU资源

适用场景

  • 高性能计数器(如实时统计)
  • 状态标志位的快速更新
  • 无锁数据结构的实现

2. 互斥锁保护数据

优点

  • 通用性强:适合保护复杂数据结构
  • 线程安全:确保临界区操作原子性
  • 开发简单:标准库支持,容易实现

缺点

  • 性能开销:锁竞争会导致上下文切换
  • 死锁风险:需要仔细设计锁的获取顺序
  • 粒度控制:粗粒度锁可能降低并发性

适用场景

  • 需要保护复杂对象状态(如容器类)
  • 对数据一致性要求严格的场景
  • 需要跨多个变量保持原子性的操作

3. 使用QThread(继承方式)

优点

  • Qt原生支持:与信号槽机制深度整合
  • 生命周期明确:通过start()/quit()管理线程
  • 资源管理方便:可搭配QSharedPointer使用

缺点

  • 继承限制:必须继承QThread类
  • 灵活性不足:run()函数是单一入口点
  • 内存管理:需要手动处理跨线程对象

适用场景

  • Qt GUI应用程序的后台任务
  • 需要与界面线程频繁交互的场景
  • 已深度使用Qt框架的项目

4. 使用moveToThread(工作对象模式)

优点

  • 解耦设计:业务逻辑与线程控制分离
  • 事件驱动:可利用Qt事件循环
  • 资源复用:单个线程处理多个任务

缺点

  • 学习曲线:需要理解Qt对象树机制
  • 信号槽开销:跨线程通信存在性能损耗
  • 调试困难:异常传播路径不直观

适用场景

  • 需要长期运行的后台服务
  • 事件驱动型任务(如网络通信)
  • 需要动态创建/销毁任务的系统

🎨 3 方案选择建议

特征需求推荐方案
超高频计数器更新原子控制
复杂数据结构保护互斥锁
Qt GUI程序后台计算QThread
事件驱动的异步任务moveToThread
跨平台通用逻辑原子控制/互斥锁
需要精细控制内存顺序原子控制(memory_order)
需要与界面元素频繁交互QThread/moveToThread
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值