qt c++线程中的同步和异步

发布于:2025-03-11 ⋅ 阅读:(36) ⋅ 点赞:(0)

一、线程同步

用于协调多个线程对共享资源的访问,避免竞态条件。

常用工具:
  1. QMutex(互斥锁)

    • 保护临界区,确保一次仅一个线程访问资源。
    QMutex mutex;
    int sharedData = 0;
    
    void Thread::run() {
        mutex.lock();
        sharedData++; // 安全操作
        mutex.unlock();
    }
    
    • QMutexLocker 自动管理锁生命周期:
      {
          QMutexLocker locker(&mutex);
          sharedData++;
      } // 自动解锁
      
  2. QReadWriteLock(读写锁)

    • 允许多个读线程,但写线程独占。适用于读多写少的场景。
    QReadWriteLock lock;
    void readData() {
        lock.lockForRead();
        // 读取数据
        lock.unlock();
    }
    void writeData() {
        lock.lockForWrite();
        // 修改数据
        lock.unlock();
    }
    
  3. QSemaphore(信号量)

    • 控制对多个资源的访问(如连接池)。
    QSemaphore sem(3); // 允许3个线程同时访问
    
    void accessResource() {
        sem.acquire();
        // 使用资源
        sem.release();
    }
    
  4. QWaitCondition(条件变量)

    • 实现生产者-消费者模型,线程等待特定条件。
    QMutex mutex;
    QWaitCondition cond;
    QQueue<int> buffer;
    
    void Producer::run() {
        mutex.lock();
        buffer.enqueue(data);
        cond.wakeOne(); // 通知消费者
        mutex.unlock();
    }
    
    void Consumer::run() {
        mutex.lock();
        while (buffer.isEmpty()) {
            cond.wait(&mutex); // 等待数据
        }
        int data = buffer.dequeue();
        mutex.unlock();
    }
    

二、异步编程

将耗时操作移至后台线程,避免阻塞主线程(如GUI线程)。

常用方法:
  1. 信号槽与事件循环

    • 通过队列连接(Qt::QueuedConnection)跨线程通信。
    class Worker : public QObject {
        Q_OBJECT
    public slots:
        void doWork() {
            // 耗时操作
            emit resultReady(result);
        }
    signals:
        void resultReady(int);
    };
    
    // 主线程
    QThread *thread = new QThread;
    Worker *worker = new Worker;
    worker->moveToThread(thread);
    connect(thread, &QThread::started, worker, &Worker::doWork);
    connect(worker, &Worker::resultReady, this, &MainWindow::handleResult);
    thread->start();
    
  2. QtConcurrent框架

    • 简化异步任务执行。
    #include <QtConcurrent>
    void processData(const QImage &image) {
        // 处理图像
    }
    
    // 启动异步任务
    QFuture<void> future = QtConcurrent::run(processData, image);
    QFutureWatcher<void> watcher;
    connect(&watcher, &QFutureWatcher::finished, this, &MainWindow::onFinished);
    watcher.setFuture(future);
    
  3. 异步网络请求(QNetworkAccessManager)

    • 非阻塞方式处理HTTP请求。
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    connect(manager, &QNetworkAccessManager::finished, this, [](QNetworkReply *reply) {
        if (reply->error() == QNetworkReply::NoError) {
            qDebug() << "Data received:" << reply->readAll();
        }
    });
    manager->get(QNetworkRequest(QUrl("https://example.com")));
    

三、实际开发建议

  1. 避免直接操作GUI组件
    所有UI更新应在主线程完成,通过信号槽传递结果。

  2. 资源管理
    使用moveToThread管理对象生命周期,确保对象在正确线程析构。

  3. 死锁预防

    • 按固定顺序获取多个锁。
    • 使用QMutex::tryLock()设置超时避免永久阻塞。
  4. 性能优化

    • 减少锁的持有时间,避免在临界区执行耗时操作。
    • 优先使用无锁结构(如原子操作QAtomicInt)。

四、示例场景

场景1:后台计算更新UI
// Worker线程执行计算
class Calculator : public QObject {
    Q_OBJECT
public slots:
    void calculate(int input) {
        int result = 0;
        for (int i=0; i<input; ++i) result += i;
        emit done(result);
    }
signals:
    void done(int);
};

// 主窗口
MainWindow::MainWindow() {
    QThread *thread = new QThread;
    Calculator *calc = new Calculator;
    calc->moveToThread(thread);
    connect(ui->btn, &QPushButton::clicked, [=] {
        QtConcurrent::run([=] { calc->calculate(1000000); });
    });
    connect(calc, &Calculator::done, this, [=](int res) {
        ui->label->setText(QString::number(res)); // 主线程更新UI
    });
    thread->start();
}
场景2:生产者-消费者模型
// 共享缓冲区
QQueue<Data> buffer;
QMutex mutex;
QWaitCondition bufferNotEmpty;

// 生产者
void Producer::run() {
    while (true) {
        Data data = generateData();
        {
            QMutexLocker locker(&mutex);
            buffer.enqueue(data);
            bufferNotEmpty.wakeOne(); // 通知消费者
        }
    }
}

// 消费者
void Consumer::run() {
    while (true) {
        QMutexLocker locker(&mutex);
        while (buffer.isEmpty()) {
            bufferNotEmpty.wait(&mutex); // 等待数据
        }
        Data data = buffer.dequeue();
        processData(data);
    }
}

通过合理选择同步机制和异步模式,可以构建高效、响应迅速的Qt多线程应用。


网站公告

今日签到

点亮在社区的每一天
去签到