1.概要
在Qt中,槽函数在哪个线程中执行,主要由以下几个方面决定:
1. 信号和槽的连接类型
Qt中的信号和槽可以通过QObject::connect
函数进行连接,QObject::connect
函数有一个参数用于指定连接类型,这个连接类型决定了槽函数在哪个线程中执行。主要的连接类型有以下几种:
- Qt::DirectConnection:槽函数在信号发出者所在的线程中执行。这种连接类型是同步的,槽函数会立即被调用。
- Qt::QueuedConnection:槽函数在信号接收者所在的线程中执行。信号发出者将信号和参数打包为事件,并将其添加到接收者线程的事件队列中。接收者线程的事件循环在适当的时候处理事件队列,从而执行槽函数。这种连接类型是异步的,槽函数不会立即被调用。
- Qt::AutoConnection:这是默认的连接类型。如果信号和槽在同一个线程中,则等同于Qt::DirectConnection;如果信号和槽在不同的线程中,则等同于Qt::QueuedConnection。
2. 接收者对象的线程归属
槽函数在哪个线程中执行,还取决于接收者对象(即槽函数所属的对象)所在的线程。如果接收者对象所在的线程与信号发出者所在的线程不同,且连接类型为Qt::QueuedConnection或Qt::AutoConnection(在跨线程情况下),则槽函数会在接收者对象所在的线程中执行。
3. 线程的事件循环
对于Qt::QueuedConnection类型的连接,接收者线程的事件循环是必须的。如果接收者线程没有事件循环,那么即使信号被发出,槽函数也不会被调用。因此,在使用Qt::QueuedConnection时,需要确保接收者线程已经启动了事件循环。
4. 手动控制线程归属
在Qt中,可以通过QObject::moveToThread
函数将对象移动到另一个线程中。这样,对象的槽函数就会在该线程中执行。这种方法允许开发者更灵活地控制槽函数的执行线程。
综上所述,Qt中槽函数在哪个线程中执行,主要由信号和槽的连接类型、接收者对象的线程归属、线程的事件循环以及手动控制线程归属等因素决定。在实际开发中,需要根据具体的需求和场景来选择合适的连接类型和线程管理方式。
2.实验
1.实验1 槽函数不同线程在槽函数的线程运行,相同在发信号的函数运行,(本质就是在槽函数所在的线程运行)
1.代码
工程文件cmake
cmake_minimum_required(VERSION 3.14)
project(untitled8 LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core)
add_executable(untitled8
main.cpp
WorkerA.h
)
target_link_libraries(untitled8 Qt${QT_VERSION_MAJOR}::Core)
include(GNUInstallDirs)
install(TARGETS untitled8
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
2.线程文件
#ifndef WORKERA_H
#define WORKERA_H
#include <QThread>
#include <QObject>
#include <QDebug>
class WorkerA : public QObject {
Q_OBJECT
public slots:
void doWork() {
// 在这里做一些工作,然后发出信号
qDebug() << "WorkerA: Doing work in thread" << QThread::currentThread();
emit signalFromA();
// 输出当前主线程的ID
qDebug() << "WorkerA::doWork thread ID:" << QThread::currentThreadId();
}
signals:
void signalFromA();
};
class WorkerB : public QObject {
Q_OBJECT
public slots:
void handleSignalFromA() {
qDebug() << "WorkerB: Received signal in thread" << QThread::currentThread();
// 输出当前主线程的ID
qDebug() << "WorkerB::handleSignalFromA thread ID:" << QThread::currentThreadId();
// 在这里处理信号
}
};
#endif // WORKERA_H
3.主函数
#include <QCoreApplication>
#include <QThread>
#include <QObject>
#include <QDebug>
#include "WorkerA.h"
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
// 输出当前主线程的ID
qDebug() << "Main thread ID:" << QThread::currentThreadId();
// 创建 WorkerA 和 WorkerB 对象
WorkerA workerA;
WorkerB workerB;
// 创建线程对象
QThread threadA;
QThread threadB;
// 将 WorkerA 移动到 threadA,将 WorkerB 移动到 threadB
workerA.moveToThread(&threadA);
workerB.moveToThread(&threadB);
// 连接信号和槽
QObject::connect(&workerA, &WorkerA::signalFromA, &workerB, &WorkerB::handleSignalFromA);
// 连接 threadA 的 started 信号到 workerA 的 doWork 槽
QObject::connect(&threadA, &QThread::started, &workerA, &WorkerA::doWork);
// 启动线程
threadA.start();
threadB.start();
// 等待线程完成(可选,这将使主线程等待子线程结束)
threadA.wait();
threadB.wait();
return app.exec();
}
2.运行结果
Main thread ID: 0x71f0
WorkerA: Doing work in thread QThread(0xd1a0dff680)
WorkerA::doWork thread ID: 0x34bc
WorkerB: Received signal in thread QThread(0xd1a0dff670)
WorkerB::handleSignalFromA thread ID: 0x6e54
2.实验2 槽函在自己的线程运行
1.变更点
变更前
// 连接信号和槽
QObject::connect(&workerA, &WorkerA::signalFromA, &workerB, &WorkerB::handleSignalFromA);
变更后
添加 Qt::QueuedConnection
// 连接信号和槽
QObject::connect(&workerA, &WorkerA::signalFromA, &workerB, &WorkerB::handleSignalFromA,Qt::QueuedConnection);
2.运行结果
Main thread ID: 0x56fc
WorkerA: Doing work in thread QThread(0xcc74dffaf0)
WorkerA::doWork thread ID: 0x2860
WorkerB: Received signal in thread QThread(0xcc74dffae0)
WorkerB::handleSignalFromA thread ID: 0x4608
可以看到线程id不同和实验1的效果完全相同
从结果来看和“实验1”一样
3.实验三 槽函数在发信号的线程执行
1.变更点
变更前
// 连接信号和槽
QObject::connect(&workerA, &WorkerA::signalFromA, &workerB, &WorkerB::handleSignalFromA);
变更后
// 连接信号和槽
QObject::connect(&workerA, &WorkerA::signalFromA, &workerB, &WorkerB::handleSignalFromA,Qt::DirectConnection);
2.运行结果
Main thread ID: 0x6d00
WorkerA: Doing work in thread QThread(0x39dd7ff800)
WorkerB: Received signal in thread QThread(0x39dd7ff800)
WorkerB::handleSignalFromA thread ID: 0x6164
WorkerA::doWork thread ID: 0x6164
可以看到线程id相同