connect信号槽的异步性导致多线程中的数据被更改及Qt::BlockingQueuedConnection作用

目录

1. 问题现象       

2. 原因分析

3. 解决方法


1. 问题现象       

       因为业务需求,需要利用子线程向主线程发送数据。像这种子线程和主线程进行数据交互时,一般都是通过Qt的信号槽机制来实现。如下代码:

void myClass::doBusiness()
{
    ...... // 其它代码略

    connect(this, &myClass::mySignal, m_pBusiness, &CMyBusiness::processBusiness);

    // 开启业务数据线程
    auto pDataThread = new std::thread(&myClass::dataThreadFun, this);

    ...... // 其它代码略

}

// 数据线程函数
void myClass::dataThreadFun()
{
    // 无限循环,一直处理业务
    while (true)
    {

        ...... // 其它代码略
        char* p = data; // data数据在while的每轮循环中都会被更改
        emit mySignal(p);

    }
}


// 业务处理类,位于主线程
void CMyBusiness::processBusiness(char* pData)
{
    // 取出pData做一些处理
}

其中m_pBusiness为CMyBusiness类对象,在线程函数dataThreadFun中的以参数p发送信号mySignal,以让CMyBusiness类的processBusiness(char* pData)得以执行,但测试发现,在processBusiness函数中,参数pData和mySignal信号发送过来的数据不一样,数据被更改了。

2. 原因分析

      问题的原因是第5行connect信号槽时,采用默认连接方式,即Qt::AutoConnection方式,Qt官方对该方式的说明如下:

(Default) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.

即:如果接收信号的对象和发送信号的对象位于同一个线程,则采取Qt::DirectConnection连接;否则就采取Qt::QueuedConnection方式。当信号被发送时,Qt会探测以决定采取Qt::DirectConnection连接方式还是采取Qt::QueuedConnection连接方式。

       对上面的代码段,CMyBusiness即为接收信号的对象,m_pBusiness位于主线程,发送信号对象位于子线程,根据上面的说明,所以第5行的connect采取的是Qt::QueuedConnection连接类型。而Qt对Qt::QueuedConnection的解释如下:

The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.

意思是说:当控制返回接收对象的事件循环时,槽函数才会被执行,槽函数是在接收者所在线程执行的 。也就说Qt::QueuedConnection连接模式就是把信号(类似事件)放到接收者的事件队列中,仅仅只是放入,但不是立即执行,而是等循环到该信号(事件)时才执行,这就会和上面代码的dataThreadFun表示的子线程产生一个时间差,就是在这个时间内,数据被更改了,其是通过上面代码的22行更改的。

3. 解决方法

       解决该问题的方法是上面第5行的connect函数最后一个参数,不要采用默认的Qt::AutoConnection连接方式,而是采用Qt::BlockingQueuedConnection连接方式。Qt官方对Qt::BlockingQueuedConnection解释如下:

Same as Qt::QueuedConnection, except that the signalling thread blocks until the slot returns. This connection must not be used if the receiver lives in the signalling thread, or else the application will deadlock.

意思是说:同Qt::QueuedConnection类似,唯一不同的是:发送信号的线程将会一直阻塞,直到接收信号的槽函数执行后才继续往下执行。这种连接方式不能用于接收信号的对象和发送信号对象都在同一个线程的情况,否则会引起死锁。

        采取Qt::BlockingQueuedConnection连接方式后,上面的子线程dataThreadFun在发送信号后会阻塞,直到CMyBusiness类的processBusiness函数执行后才继续往下执行,所以数据就不会被更改。

Qt::BlockingQueuedConnection连接方式不能用于接收信号的对象和发送信号对象都在同一个线程的情况,否则会引起死锁,这很好理解,如果位于同一线程,就会造成你等我,我也等你,大家相互等待对方执行完后再执行,就会造成死锁。

  • 31
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt,可以使用多种方法来异步显示实时图片或使用多线程显示实时图片。下面是两种常用的方法: 1. 异步显示实时图片 使用QtQThread和QtConcurrent库,可以实现异步显示实时图片。以下是一个简单的例子: ```c++ // 定义一个QLabel用于显示图片 QLabel *m_pLabel; // 定义一个函数,用于更新图片 void updateImage(const QImage &image) { m_pLabel->setPixmap(QPixmap::fromImage(image)); } // 定义一个函数,用于获取实时图片 QImage getImage() { // 获取实时图片的代码 return image; } // 在主线程调用 QFuture<void> future = QtConcurrent::run([&](){ while(true) { QImage image = getImage(); // 发送信号,更新图片 emit updateImage(image); } }); // 连接信号函数 connect(this, &MainWindow::updateImage, this, &MainWindow::updateImage); ``` 2. 多线程显示实时图片 使用QtQThread和信号机制,可以实现多线程显示实时图片。以下是一个简单的例子: ```c++ // 定义一个QLabel用于显示图片 QLabel *m_pLabel; // 定义一个线程类 class ImageThread : public QThread { Q_OBJECT public: // 定义一个信号,用于更新图片 void updateImage(const QImage &image) { emit imageUpdated(image); } signals: // 定义一个信号,用于更新图片 void imageUpdated(const QImage &image); protected: void run() override { while(true) { QImage image = getImage(); // 发送信号,更新图片 emit imageUpdated(image); } } private: // 定义一个函数,用于获取实时图片 QImage getImage() { // 获取实时图片的代码 return image; } }; // 在主线程创建线程对象 ImageThread *m_pImageThread = new ImageThread(); // 连接线程的信号函数 connect(m_pImageThread, &ImageThread::imageUpdated, this, &MainWindow::updateImage); // 在主线程启动线程 m_pImageThread->start(); // 定义一个函数,用于更新图片 void updateImage(const QImage &image) { m_pLabel->setPixmap(QPixmap::fromImage(image)); } ``` 需要注意的是,在多线程访问Qt控件时需要使用信号机制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值