Qt 中多线程对应的信号槽

Qt 中通过 moveToThread 方式来实现的多线程之间数据交互一般是通过信号槽来进行的,信号槽不仅可以用于同一个线程中,也可以用于多线程之间,当用于多线程之间时,其连接方式为 Qt::QueuedConnection ,即队列连接。
多线程之间建立的信号槽主要是如下两个:

  • 主线程发送信号:来触发子线程的槽函数;子线程接受该信号:开始执行复杂的槽函数
    connect(this, &MainWindow::SigStartDoWorkA, myWork, &MyWork::OnDoWorkA)
  • 子线程发送信号:更新数据或通知槽函数的执行结果,主线程接受信号:刷新UI或更新数据
    connect(myWork, &MyWork::SigFinishDoWorkA, this, &MainWindow::OnGetWorkAResult)

主线程和子线程之间通过信号槽进行连接时,其连接方式为队列连接,此时有几个需要注意的地方,否则会出现连接失败的情况:

  1. 连接信号的参数必须是能够被Qt元对象系统所识别的类型
  2. 如果参数为非Qt元对象系统所识别的类型,则需要对其进行声明和注册
  3. 参数只能是值类型或常引用类型,不能为引用类型,因为Qt需要将该参数复制和存储起来,所以其不能被修改

1. 信号的参数必须是能够被Qt元对象系统所能识别的类型

队列连接时,信号触发后会进入到队列中,等槽函数所在的线程进入到对应的事件循环时,才能让对应的槽函数执行,而不是立刻让槽函数执行。因此Qt需要先将信号的参数复制并存储在事件中,以便在稍后的时间点执行槽函数。

在复制时,参数类型必须是Qt元对象系统能够识别的类型,因为Qt需要根据元对象系统(moc)来进行类型检查、参数复制和事件处理。元对象系统在编译时生成额外的元数据,描述了类的信息,使Qt能够在运行时动态地访问和处理这些信息。
一般Qt自带的数据类型都是能被元对象系统识别的,如: QString, QVector等以 Q 开头的类型,以及通用的类型,如int, bool,double 等类型。
std::string, std::vector 等C++ STL中的数据类型就无法被 moc 识别。

2. 不能被moc所识别的参数类型,需要进行声明和注册

如下为一个不能被 Qt 元对象系统所识别的信号的参数类型

signals:
  void SigStartDoWork(std::string city, std::vector<double> vecData, int age)

以上的信号因为包含有不能被Qt 元对象系统所识别的参数类型,因此通过 队列连接时将会失败,此时将封装成自定类型,如结构体并进行注册后才能使用

/* MyWork.h 
 * 继承自 QObject 的类, 用来封装复杂耗时计算 
*/

// 自定义结构体类型来封装要传递的类型
struct CustomData
{
    std::string city;
    std::vector<double> vecData;
    int age;
};
// 首先进行声明
Q_DECLARE_METATYPE(CustomData);

// 在该类的构造函数中进行注册,让 moc 能识别该类型
// 注册函数的执行要在建立信号槽之前,否则会连接失败
qRegisterMetaType<CustomData>();

/****************************************************************************************/
// 如果后续要避免该结构体与其它的结构体命名相同导致的冲突,可以将其放在一个namespace 中
namespace MyData
{
    struct CustomData
    {
        std::string city;
        std::vector<double> vecData;
        int age;
    };
}
// 声明需要在namespace外,且变量名前要加上命名空间的作用域
Q_DECLARE_METATYPE(MyData::CustomData);

// 构造函数中进行注册
qRegisterMetaType<MyData::CustomData>();

此时信号的参数就是以上自定义的结构体类型,如下:

signals:
  void SigStartDoWork(MyData::CustomData curData)

3. 参数类型不能为引用类型

队列连接时,由于需要将信号的参数复制并存储到事件中,因此必要确保该参数 能被元对象系统识别不能被修改,所以参数将不能为引用类型。
如以下的参数为引用类型将会让连接失败,需要将其修改成常引用值传递

signals:
  void SigStartDoWork(QString &msg);
posted @ 2024-04-25 17:49  Jeffxue  阅读(114)  评论(0编辑  收藏  举报