当前位置:网站首页>Qt多线程中槽函数在哪个线程里执行分析
Qt多线程中槽函数在哪个线程里执行分析
2022-07-28 06:13:00 【艾米莉亚糖】
1.QThread中的slots是在那个线程中执行?
QThread::run() 函数的作用:run 对于线程的作用相当于main函数对于应用程序。它是线程的入口,run的开始和结束意味着线程的开始和结束。
class Thread:public QThread
{
Q_OBJECT
public:
Thread(QObject* parent=0):QThread(parent){}
public slots:
void slot() { ... } //slot函数依附于主线程
signals:
void sig();
protected:
void run() { ... } //run函数里的代码在次线程中执行
};
int main(int argc, char** argv)
{
...
Thread thread;
...
}run函数中的代码时确定无疑要在次线程中运行的,那么其他的呢?比如 slot 是在次线程还是主线程中运行?
QObject::connect() 涉及信号槽,我们就躲不过 connect 函数,它的第五个参数是设置连接类型的。(为了简单起见,只看它最常用的3个值)
- 自动连接(Auto Connection),这是默认设置,如果信号在接收者所依附的线程内发射,则等同于直接连接,如果发射信号的线程和接受者所依附的线程不同,则等同于队列连接。
- 直接连接(Direct Connection),当信号发射时,槽函数将直接被调用。无论槽函数所属对象在哪个线程,槽函数都在发射信号的线程内执行。
- 队列连接(Queued Connection),当控制权回到接受者所依附线程的事件循环时,槽函数被调用。槽函数在接收者所依附线程执行。
连接类型强调两个概念:发送信号的线程 和 接收者所依附的线程。而 slot 函数属于我们在main中创建的对象 thread,即thread依附于主线程。
- 队列连接告诉我们:槽函数在接受者所依附线程执行。即 slot 将在主线程执行。
- 直接连接告诉我们:槽函数在发送信号的线程执行。信号在那个线程发送呢?不定!
- 自动连接告诉我们:二者不同,等同于队列连接。即 slot 在主线程执行。
太绕了?不是么(要彻底理解这几句话,你可能需要看Qt meta-object系统和Qt event系统)。
怎么理解到底在哪个线程执行slot()函数?
需要搞清楚QThread的这个类。
- QThread 是用来管理线程的,它所依附的线程和它管理的线程并不是同一个东西。
- QThread 所依附的线程,就是执行 QThread t(0) 或 QThread * t=new QThread(0) 的线程。也就是咱们这儿的主线程。
- QThread 管理的线程,就是 run 启动的线程。也就是次线程。
- 因为QThread的对象依附在主线程中,所以他的slot函数会在主线程中执行,而不是次线程。
除非:QThread 对象依附到次线程中(通过movetoThread)。
slot 和信号是直接连接,且信号在次线程中发射。
- 但上两种解决方法都不好,因为QThread不是这么用的(Bradley T. Hughes)
通过代码的方式应该比较好理解
主线程(信号)QThread(槽), 但由于没说槽函数是在主线程执行的,所以不少人都认为它应该是在次线程执行了。
- 定义一个 Dummy 类,用来发信号。
- 定义一个 Thread 类,用来接收信号,重载 run 函数,目的是打印 threadid。
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QDebug>
class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(){}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
};
class Thread:public QThread
{
Q_OBJECT
public:
Thread(QObject* parent=0):QThread(parent)
{
//moveToThread(this); //后面会讲
}
public slots:
void slot_main()
{
qDebug()<<"from thread slot_main:" <<currentThreadId();
}
protected:
void run()
{
qDebug()<<"thread thread:"<<currentThreadId();
exec();
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
Thread thread;
Dummy dummy;
//主线程的sig信号,thread依附在主线程中的slot_main槽
QObject::connect(&dummy, SIGNAL(sig()), &thread, SLOT(slot_main()));
thread.start();
dummy.emitsig();
return a.exec();
}看打印输出结果(具体值每次都变,但结论不变)
main thread: 0x1a40 from thread slot_main: 0x1a40 thread thread: 0x1a48可以看到,主线程id和slot_main()中的线程id是一样的,槽函数的线程和主线程是一样的!所以thread类中的slot_main()是在主线程执行,run()函数是在次线程执行。
如果你看过Qt自带的例子,你会发现 QThread 中 slot 和 run 函数共同操作的对象,都会用QMutex锁住。为什么?因为slot和run处于不同线程,需要线程间的同步!
如果想让槽函数slot在次线程运行(比如它执行耗时的操作,会让主线程死掉),怎么解决呢?(就是想让这个slot_main()槽函数去次线程中执行,虽然它是依附于主线程的)
参考我们前面的结论,很容易想到:将 thread 依附的线程改为次线程不就行了?也是代码中注释掉的 moveToThread(this)所做的就是将整个 thread 类移到次线程中,去掉注释,你会发现slot在次线程中运行。打印输出结果:
main thread: 0x13c0
thread thread: 0x1de0
from thread slot_main: 0x1de0此时的 slot_main() 槽函数就是在次线程中执行了。
但是 Bradley T. Hughes作者强烈批判的用法,觉得这样写不规范。推荐的方法后面会给出。
run中信号与QThread中槽
定义一个 Dummy 类,在run中发射它的信号,也可以在run中发射 Thread 类中的信号,而不是Dummy(效果完全一样)。QThread 定义槽函数,重载run函数。
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QDebug>
class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(QObject* parent=0):QObject(parent){}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
};
class Thread:public QThread
{
Q_OBJECT
public:
Thread(QObject* parent=0):QThread(parent)
{
//moveToThread(this);
}
public slots:
void slot_thread()
{
qDebug()<<"from thread slot_thread:" <<currentThreadId();
}
signals:
void sig();
protected:
void run()
{
qDebug()<<"thread thread:"<<currentThreadId();
Dummy dummy;
//次线程中的sig(),主线程中的slot_main()
connect(&dummy, SIGNAL(sig()), this, SLOT(slot_thread()));
dummy.emitsig();
exec();
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
Thread thread;
thread.start();
return a.exec();
}打印输出结果:
main thread: 0x15c0
thread thread: 0x1750
from thread slot_thread: 0x15c0其实没悬念,slot_main() 肯定是在主线程执行。thread 对象本身在主线程。所以它的槽也在要在主线程执行。
如何让slot_main() 在子线程中执行呢?
- (方法一)前面提了 moveToThread,这儿可以用,而且可以解决问题。当同样,是被批判的对象。
- (方法二)注意哦,这儿我们的信号时次线程发出的,对比connect连接方式,会发现:采用直接连接,槽函数将在次线程(信号发出的线程)执行。这个方法不太好,因为你需要处理slot和它的对象所在线程的同步。需要 QMutex 一类的东西。
推荐的方法:
其实,这个方法太简单,太好用了。
定义一个普通的QObject派生类,然后将其对象 moveToThread() 到 QThread 中。使用信号和槽时根本不用考虑多线程的存在。也不用使用QMutex来进行同步,Qt的事件循环会自己自动处理好这个。
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtCore/QDebug>
class Dummy:public QObject
{
Q_OBJECT
public:
Dummy(QObject* parent=0):QObject(parent) {}
public slots:
void emitsig()
{
emit sig();
}
signals:
void sig();
};
class Object:public QObject
{
Q_OBJECT
public:
Object(){}
public slots:
void slot()
{
qDebug()<<"from thread slot:" <<QThread::currentThreadId();
}
};
#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<<QThread::currentThreadId();
QThread thread;
Object obj;
Dummy dummy;
obj.moveToThread(&thread); //是将obj这个对象移入次线程,而不是讲QThread移入
//次线程中的sig(),次线程中的slot()
QObject::connect(&dummy, SIGNAL(sig()), &obj, SLOT(slot()));
thread.start();
dummy.emitsig();
return a.exec();
}打印输出结果:
main thread: 0x1a5c
from thread slot: 0x186cslot() 确实不在主线程中运行了。
其他:
本文只考虑了使用事件循环的情况,也有可能run中没有事件循环。这时信号与槽会与本文有点差别。比如run中使用connect时,队列连接就受限制了。其实只要理解了前面这些,没有事件循环的情况很容易就想通了。
边栏推荐
- 细说共模干扰和差模干扰
- EMC's "don't come back until you rectify"
- EMC整改思路
- PCB design skills of EMC
- node(一)
- 快速搭建DMHS DM之间双向同步
- 5g commercial third year: driverless "going up the mountain" and "going to the sea"
- 【jvm优化】线上JVM调优实践
- EMC中的基石-电磁兼容滤波知识大全!
- Near infrared two region agzs quantum dots wrapped deoxyribonucleic acid dna|dna agzsqds (Qiyue)
猜你喜欢

【jvm优化超详细】常见的JVM调优场景

辨析覆盖索引/索引覆盖/三星索引

protobuf 基本语法总结

JUC原子类: CAS, Unsafe、CAS缺点、ABA问题如何解决详解

CAS vs Database optimistic lock

Big talk persistence and redolog

DNA-Ag2SQDs脱氧核糖核酸DNA修饰硫化银Ag2S量子点的合成方法

【干货】32个EMC标准电路分享!

Elaborate on common mode interference and differential mode interference

DNA deoxyribonucleic acid modified platinum nanoparticles ptnps DNA | scientific research reagent
随机推荐
Merge two sorted linked lists - two questions per day
Why is ESD protection so important for integrated circuits? How to protect?
Modify the conf file through sed
演讲笔记 适合所有人的实用程序生成 PCG
非关系型数据库之Redis【redis集群详细搭建】
EMC中class A和class B哪个更严格?
User mode vs kernel mode, process vs thread
Copper indium sulfide CuInSe2 quantum dots modified DNA (deoxyribonucleic acid) DNA cuinse2qds (Qiyue)
细说共模干扰和差模干扰
每日一题——分割等和子集
MySQL view the memory size of a table
Retryer of guava
It has been rectified seven times and took half a month. Painful EMC summary
辨析覆盖索引/索引覆盖/三星索引
How to understand the adjective prefix of socket: "connection oriented" and "connectionless"
Mysql中有哪些不同的表格?
Clion debugging redis6 source code
Adjust the array order so that odd numbers precede even numbers - two questions per day
[untitled]
Summary of project experience