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::QueuedConnection连接方式后,上面的子线程dataThreadFun在发送信号后会阻塞,直到CMyBusiness类的processBusiness函数执行后才继续往下执行,所以数据就不会被更改。

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

你可能感兴趣的:(#,Qt平时遇到的疑难点,qt,BlockingQueued,connect,多线程)