在我的代码中,我有一个在单独的
std::jthread
:
// Worker.h:
#pragma once
#include <QWidget>
class Worker : public QObject {
Q_OBJECT
public:
Worker();
signals:
void sendMessage(QString msg);
private:
void threadFunction();
std::jthread m_thread;
};
以及在主线程中运行的小部件:
// Widget.h
#pragma once
#include <QWidget>
class Widget : public QWidget {
Q_OBJECT
public:
Widget();
public slots:
void getMessage(QString msg);
};
我在sendMessage信号和getMessage槽之间创建了一个排队连接:
#include <QApplication>
#include <chrono>
#include "Widget.h"
#include "Worker.h"
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
Widget widget;
{
Worker worker;
QObject::connect(&worker, &Worker::sendMessage, &widget, &Widget::getMessage,
Qt::ConnectionType::QueuedConnection);
std::this_thread::sleep_for(std::chrono::seconds(1));
} // worker deleted and thread terminated
widget.show();
return a.exec();
}
Q1:在处理信号之前终止发出信号的线程是否安全?
// Worker.cpp:
#include "Worker.h"
#include <chrono>
Worker::Worker() {
m_thread = std::jthread(&Worker::threadFunction, this);
}
void Worker::threadFunction() {
std::this_thread::sleep_for(std::chrono::seconds(1));
emit sendMessage("finished");
}
和
// Widget.cpp:
#include "Widget.h"
#include <iostream>
Widget::Widget() : QWidget() {}
void Widget::getMessage(QString msg) {
std::cerr << msg.toStdString() << "\n";
}
sendMessage的发射调用了该方法:
// moc_Worker.cpp:
void Worker::sendMessage(QString _t1)
{
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
QMetaObject::activate()
定义见
qobject.cpp
.
阅读关于
how Qt signals and slots are implemented
我发现这种方法查找的是
SignalVector
Worker对象。此元素是一个双链表
QObjectPrivate::Connection
物体。在我的例子中,这个列表只有一个元素,它与
getMessage
方法。
它还呼吁
QThreadData *td = connection->receiverThreadData.loadRelaxed();
bool receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
以确定接收方是否与发送方处于同一线程中。
这个
receiverThreadData
在建立连接时设置:
// qobject.cpp:
QObjectPrivate::Connection *QMetaObjectPrivate::connect(...,QObject *receiver,...) {
...
QThreadData *td = receiver->threadData;
connection->receiverThreadData.storeRelaxed(td);
...
}
由于我正在使用
QueuedConnection
方法:
void QObject::queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv);
它依次调用:
void QCoreApplication::postEvent(QObject *receiver, QEvent *event);
用一个
QMetaCallEvent
:
struct QMetaCallEvent {
// the signal:
QObject *sender;
uint signal_index;
// the slot:
QObject *receiver;
ushort method_offset;
// the data:
int nargs_;
int* types_;
void** args_;
};
Q2:在事件处理之前删除发送方对象是否安全?
阅读关于
QObject *QObject::sender()
:
如果在信号激活的槽中调用,则返回一个指向发送信号的对象的指针;否则,它返回nullptr。该指针仅在从该对象的线程上下文调用此函数的槽执行期间有效。
如果发送器被破坏,或者插槽与发送器的信号断开连接,则此函数返回的指针将无效。"
无效指针=可能会崩溃?