QT中线程之间的通讯

在Qt中,Widget窗体通常运行在主线程(GUI线程)中,而其他线程用于执行长时间运行的任务或后台操作,以避免阻塞GUI线程。要在Widget窗体和其他线程之间安全地发送数据,可以使用以下几种方法:

  1. 信号和槽: 使用Qt的信号和槽机制是最简单和最常用的方法。你可以从GUI线程发射信号到其他线程的槽函数。

    // 在GUI线程中
    emit sendDataToThread(data);
    
    // 在其他线程中
    connect(this, &MyThread::sendDataToThread, &myWidget, &MyWidget::processData);
  2. 事件: 使用Qt的事件系统发送自定义事件,这些事件可以在线程之间传递。

    // 创建自定义事件 
    class MyDataEvent : public QEvent 
    { 
    public: 
        MyDataEvent(const MyDataType &data) : QEvent(eventType), data_(data) {} 
        static const QEvent::Type eventType; const MyDataType &data() const { return data_; } private: 
        MyDataType data_; }; 
    // 在GUI线程中 
        QCoreApplication::postEvent(myWidget, new MyDataEvent(data)); 
    // 在其他线程中处理事件 
        bool MyWidget::event(QEvent *event) 
        { 
            if (event->type() == MyDataEvent::eventType) 
            {
                 const MyDataEvent *myDataEvent = static_cast<const MyDataEvent *>(event);                         
                 processData(myDataEvent->data()); 
                 return true; 
            } 
            return QWidget::event(event); 
    }
  3. QMutex和QWaitCondition: 使用互斥锁(QMutex)和条件变量(QWaitCondition)来同步线程,并在线程之间传递数据。在Qt中,使用QMutexQWaitCondition可以很好地控制线程间的同步和数据传递。假设我们有一个共享的队列,生产者线程将数据添加到队列中,而消费者线程从队列中取出数据。我们将使用互斥锁来保护对共享队列的访问,并使用条件变量来通知消费者线程有新数据可用。

    #include <QMutex>
    #include <QMutexLocker>
    #include <QWaitCondition>
    #include <QQueue>
    #include <QDebug>
    #include <QThread>
    
    class SharedData {
    public:
        SharedData() : dataReady(false) {}
    
        QMutex mutex;
        QWaitCondition condition;
        bool dataReady;
        QQueue<int> queue;        //可以创建多个队列实现"多对多"的线程通讯模式
    
        void wakeConsumer() {
            QMutexLocker locker(&mutex);
            if (queue.isEmpty()) {
                return;
            }
            dataReady = true;
            condition.wakeAll();
        }
    
        void waitProducer() {
            QMutexLocker locker(&mutex);
            while (!dataReady) {
                condition.wait(&mutex);
            }
            dataReady = false;
        }
    };
    
    class ProducerThread : public QThread
    {
    public:
        SharedData *sharedData;
    
    protected:
        void run() override {
            for (int i = 0; i < 10; ++i) 
            {
                {   //互斥锁一定要添加锁的范围
                    QMutexLocker locker(&sharedData->mutex);
                    sharedData->queue.enqueue(i);
                }
                //通知消费者线程
                sharedData->wakeConsumer();
                qDebug() << "Produced" << i;
                sleep(1); // 模拟工作负载
            }
        }
    };
    
    class ConsumerThread : public QThread
    {
    public:
        SharedData *sharedData;
    
    protected:
        void run() override {
            while (true) {
                {
                    QMutexLocker locker(&sharedData->mutex);
                    sharedData->waitProducer();
                    if (sharedData->queue.isEmpty()) {
                        break;
                    }
                    int data = sharedData->queue.dequeue();
                }
                qDebug() << "Consumed" << data;
                sleep(1); // 模拟工作负载
            }
        }
    };
    
    int main() {
        SharedData sharedData;
    
        ProducerThread producer;
        producer.sharedData = &sharedData;
    
        ConsumerThread consumer;
        consumer.sharedData = &sharedData;
    
        producer.start();
        consumer.start();
    
        producer.wait();
        consumer.wait();
    
        return 0;
    }
    // 共享数据结构
    MyDataType sharedData;
    QMutex mutex;
    QWaitCondition condition;
    
    // 在GUI线程中
    mutex.lock();
    sharedData = newData;
    condition.wakeAll();
    mutex.unlock();
    
    // 在其他线程中
    mutex.lock();
    while (sharedData.isEmpty()) 
    {
        condition.wait(&mutex);
    }
    processData(sharedData);
    sharedData.clear();
    mutex.unlock();
  4. QQueue: 使用线程安全的队列(如QQueue)来存储和传递数据。

    // 在GUI线程中
    threadSafeQueue.enqueue(data);
    
    // 在其他线程中
    if (threadSafeQueue.isEmpty())
    {
        // 等待数据或处理其他任务
    }
    processData(threadSafeQueue.dequeue());
  5. QSharedData: 使用QSharedData来共享数据,它是一个线程安全的共享内存模型。

    class MySharedData : public QSharedData
    {
    public:
        void setData(const MyDataType &data) { this->data = data; }
        MyDataType data;
    };
    
    // 在GUI线程中
    sharedDataPtr->setData(newData);
    
    // 在其他线程中
    processData(sharedDataPtr->data());
  6. QMetaObject::invokeMethod: 使用QMetaObject::invokeMethod在不同线程之间安全地调

    // 在GUI线程中
    QMetaObject::invokeMethod(&myWidget, "processData", Qt::QueuedConnection, Q_ARG(MyDataType, data));
    
    // 在其他线程中
    void MyWidget::processData(const MyDataType &data) 
    {
        // 处理数据
    }
  7. 使用Qt Concurrent: 使用Qt Concurrent库,它提供了并行算法和线程池管理,可以简化多线程编程。

    // 在GUI线程中
    QtConcurrent::run(this, &MyWidget::processData, data);
    
    // 在其他线程中执行

请注意,跨线程通信时,应确保数据的同步和线程安全,避免竞态条件和死锁。使用上述方法之一或组合,可以实现Widget窗体和其他线程之间的数据传递。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值