gpt4 book ai didi

c++ - 在处理信号之前终止发出排队信号的线程是否安全?

转载 作者:行者123 更新时间:2023-12-05 05:47:44 26 4
gpt4 key购买 nike

在我的代码中,我有一个在单独的 std::jthread 中运行的 worker:

// 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 的 emit 调用方法:

// 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我发现此方法查找 Worker 对象的 SignalVector 的第零个元素(第一个也是唯一一个信号)。该元素是 QObjectPrivate::Connection 对象的双向链表。在我的例子中,这个列表只有 1 个元素,它是与 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_;
};

问题 2:在事件处理完毕之前删除发送者对象是否安全?

正在阅读 QObject *QObject::sender() :“如果在由信号激活的槽中调用,则返回指向发送信号的对象的指针;否则返回 nullptr。该指针仅在从该对象的线程上下文调用该函数的槽执行期间有效。

如果发送者被销毁,或者如果插槽与发送者的信号断开连接,则此函数返回的指针将失效。”

无效指针 = 可能会崩溃?

最佳答案

在发射线程终止和/或发送对象被销毁后处理信号应该是安全的。

排队连接旨在提供线程之间简单、非阻塞和线程安全的通信。尽管没有明确记录,但如果在另一个线程中处理所有发出的信号之前关闭线程是不安全的,则此机制将变得无用,因为发出线程/对象不知道槽何时被处理。

当然,

  • 您应该确保传递的对象不被破坏(在传递(智能)指针类型的情况下)。
  • 您不访问任何在另一个线程中被破坏的对象(即 QObject::sender())。

关于c++ - 在处理信号之前终止发出排队信号的线程是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70924229/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com