多线程中的信号与槽(中)

令人不解的问题:

当槽函数是线程类的成员时,为什么依然不在本线程内被调用执行?

隐藏的问题:
对象依附于哪一个线程?
对象的依附性与槽函数执行的关系?
对象的依附性是否可以改变?

对象依附于哪个线程?
默认情况下,对象依附于自身被创建的线程
例如:对象在主线程(main()函数)中被创建,则依附于主线程

int main(int argc, char* argv[])
{
   //...
   TestThread t;     //依附于主线程
   MyObject m;       //依附于主线程
}

对象的依附性与槽函数执行的关系?
默认情况下,槽函数在其所依附的线程中被调用执行

int main(int argc, char* argv[])
{
   //...
   TestThread t;     //依附于主线程
   MyObject m;       //依附于主线程
   
   //下面连接中的槽函数都在主线程中被调用执行
   QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted()));
   QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()));
}

对象的依附性是否可以改变?
QObject::moveToThread用于改变对象的线程的依附性,使得对象的槽函数在依附的线程中被调用执行

int main(int argc, char* argv[])
{
   //...
   TestThread t;     //依附于主线程
   MyObject m;       //依附于主线程
   
   //改变对象m的线程的依附性,使其依附于线程t
   m.moveToThread(&t)
}

对象的依附性

MyObject.h

#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>


class MyObject : public QObject
{
    Q_OBJECT
public:
    MyObject();
protected slots:
    void getStarted();
    void testSlot();
};

#endif // MYOBJECT_H
View Code

TestThread.h

#ifndef TESTTHREAD_H
#define TESTTHREAD_H

#include <QThread>

class TestThread : public QThread
{
    Q_OBJECT
protected:
    void run();
public:
    TestThread();

signals:
    void testSignal();
protected slots:
    void testSlot();
};

#endif // TESTTHREAD_H
View Code

MyObject.cpp

#include "MyObject.h"
#include <QObject>
#include <QThread>
#include <QDebug>

MyObject::MyObject()
{

}

void MyObject::getStarted()
{
    qDebug() <<"void MyObject::getStarted()tid = " << QThread::currentThreadId() ;
}

void MyObject::testSlot()
{
    qDebug() << "void MyObject::testSlot() " << QThread::currentThreadId() ;
}
View Code

TestThread.cpp

#include "TestThread.h"
#include <QDebug>

TestThread::TestThread()
{
    connect(this,SIGNAL(testSignal()),this,SLOT(testSlot()));
}

void TestThread::run()
{
   qDebug() << "void TestThread::run() begin...tid = " << currentThreadId();

   for(int i=0; i<10; i++)
   {
       qDebug() << "void TestThread::run() i = " << i;
       sleep(1);
   }

   emit testSignal(); //发射的信号谁来接收呢,可以在构造函数中将信号和槽函数进行关联。

   qDebug() << "void TestThread::run() end...";
}

void TestThread::testSlot()
{
    qDebug() << "void TestThread::testSlot() tid = " << currentThreadId();
}
View Code

main.cpp

#include <QCoreApplication>
#include <QThread>
#include <QDebug>
#include "TestThread.h"
#include "MyObject.h"


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main tid() " << QThread::currentThreadId();

    TestThread t;
    MyObject m;

    QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted()));
    QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()));

    m.moveToThread(&t);

    t.start();
    return a.exec();
}
View Code

课程中使用的是Qt4,MyObject相关的槽函数没有被调用,但是使用Qt5.4,MyObject相关的槽函数被调用了。

下面分析没有被调用的情况:

问题:
实验中对象m的槽函数为什么没有全部被执行?

线程中的事件循环
信号与槽的机制需要事件循环的支持
QThread类中提供的exec()函数用于开启线程的事件循环
只有开启事件循环,槽函数才能在信号发送后被调用

信号的发送是随时随地都可以完成的,发送完成后,信号就到事件队列中去了。信号进入事件队列中去有什么用呢?
没人理它,它什么用都没有。如何处理事件队列呢?此时事件循环就派上用场了。
但凡通过exec开启了事件循环,就会不停的从事件队列中取信号,取到信号后就会去判断该信号有没有关联相关的槽函数,
如果有对应的槽函数,则调用相应的槽函数。

想要槽函数在指定的线程中被调用,需要在指定的线程中调用exec函数,开启事件循环。
小结论:
前提条件
对象依附的线程开启了事件循环
后置结果
对象中的槽函数在依附的线程中被调用执行

只需要在TestThread.cpp的run函数中加上exec函数即可

void TestThread::run()
{
   qDebug() << "void TestThread::run() begin...tid = " << currentThreadId();

   for(int i=0; i<10; i++)
   {
       qDebug() << "void TestThread::run() i = " << i;
       sleep(1);
   }

   emit testSignal(); //发射的信号谁来接收呢,可以在构造函数中将信号和槽函数进行关联。

   exec();
   qDebug() << "void TestThread::run() end...";
}
View Code

 从打印结果看,与上面没有使用exec的执行结果并无不同,很可能是因为版本不同造成的。

从打印结果看,MyObject的两个槽函数都被调用了,且是在依附于t的那个线程。但是t的槽函数还是依附于主线程,我也想让t的槽函数依附t,怎么操作?非常简单,只需要在main.cpp中的main函数中,加入t.moveToThread(&t)即可,如下所示:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main tid() " << QThread::currentThreadId();

    TestThread t;
    MyObject m;

    QObject::connect(&t,SIGNAL(started()),&m,SLOT(getStarted()));
    QObject::connect(&t,SIGNAL(testSignal()),&m,SLOT(testSlot()));

    m.moveToThread(&t);
    t.moveToThread(&t);

    t.start();
    return a.exec();
}
View Code

研究槽函数的具体执行线程有什么意义?
当信号的发送与对应槽函数的执行在不同线程中,可能产生临界资源的竞争问题

比如说,在run函数对某一个临界资源进行修改,在槽函数中也对临界资源进行修改,槽函数的调用是在另一个线程中完成的,此时调用槽函数的线程和本身的线程就可能产生竞争问题。

 

原文地址:https://www.cnblogs.com/-glb/p/13463626.html