多线程中的信号与槽(中)
令人不解的问题:
当槽函数是线程类的成员时,为什么依然不在本线程内被调用执行?
隐藏的问题:
对象依附于哪一个线程?
对象的依附性与槽函数执行的关系?
对象的依附性是否可以改变?
对象依附于哪个线程?
默认情况下,对象依附于自身被创建的线程
例如:对象在主线程(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
#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
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
#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
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() ;
}
#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() ;
}
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();
}
#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();
}
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();
}
#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();
}

课程中使用的是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...";
}
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...";
}

从打印结果看,与上面没有使用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();
}
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();
}

研究槽函数的具体执行线程有什么意义?
当信号的发送与对应槽函数的执行在不同线程中,可能产生临界资源的竞争问题
比如说,在run函数对某一个临界资源进行修改,在槽函数中也对临界资源进行修改,槽函数的调用是在另一个线程中完成的,此时调用槽函数的线程和本身的线程就可能产生竞争问题。



浙公网安备 33010602011771号