如果我有一个主线程和一个单独的永久工作者QThread:
NetworkController
当状态机类进入某个状态时,它应该连接到AP(接入点)。 NetworkManager
具有连接到AP的功能。
我试图找出状态机可以以线程安全的方式执行此操作的方法。问题是WifiAP
始终更新其StateMachineWorker
实例列表:它们经常被创建和销毁。
pNetCtrlr->connect_to_ap()
直接调用NetworkManager
不是线程安全的(因为NetworkController
线程中的WifiAP
可以同时删除StateMachineWorker::on_ready_to_connect_event()
实例)。
所以我希望在NetworkController
以某种方式表示connect_to_ap()
在NetworkController
自己的主题中运行StateMachineWorker
方法 ,并阻止connect_to_ap()
线程,直到connect_to_ap()
完成其内容。我希望状态机被阻止的原因是,如果我没有阻止并让它进入事件循环,它可能会收到一些事件,使其转换到另一个状态之前 WifiAP
已完成执行;这绝不可能发生。
用于保护NetworkManager
中"" + null
列表的互斥锁无法正常工作,因为它们需要插入第三方库中。
答案 0 :(得分:4)
您可以将QMetaObject::invokeMethod
与参数Qt::BlockingQueuedConnection
一起使用。此连接类型为您添加了所有阻止逻辑,您根本不必更改第三方库。
一般例子:
<强> objects.h 强>
cat file_with_match | grep -Pv '^$' | sed -nre '/Pure delay/I,/END OF PATH/Ip'
<强>的main.cpp 强>
#include <QObject>
#include <QThread>
#include <iostream>
class Object : public QObject {
Q_OBJECT
public slots:
void foo() {
std::cout << "Hello";
thread()->msleep(2000);
std::cout << " world!" << std::endl;
}
};
class Caller : public QObject {
Q_OBJECT
public:
void call(Object* o) {
std::cout << "Calling..." << std::endl;
metaObject()->invokeMethod(o, "foo", Qt::BlockingQueuedConnection);
std::cout << "Finished!" << std::endl;
}
};
对应的是要调用的方法必须是一个槽。如果#include <QCoreApplication>
#include "objects.h"
int main(int argc, char* argv[])
{
QCoreApplication a(argc, argv);
QThread t;
Object o;
o.moveToThread(&t);
t.start();
Caller().call(&o);
return a.exec();
}
不是插槽,您可以创建一个完成工作的桥接对象,如下所述。
请记住,此 bridge 对象必须与connect_to_ap
(在您的情况下是主线程)位于同一个线程中,因此插槽在正确的事件循环中排队。您可以查看QObject::moveToThread
以获取更多信息。
快速草案将类似于:
NetworkController
<强>更新强>
通过class NetworkControllerBridge : public QObject {
Q_OBJECT
NetworkController* nc;
public:
NetworkControllerBridge(NetworkController* nc_) : nc(nc_) {}
public slots:
void connect_to_ap() {
nc->connect_to_ap();
}
};
// ...
void on_ready_to_connect_event()
{
NetworkControllerBridge bridge(pNetCtrlr);
bridge.moveToThread(qApp->thread());
metaObject()->invokeMethod(&bridge, "connect_to_ap", Qt::BlockingQueuedConnection);
}
调用方法的另一种方法是将其标记为Q_INVOKABLE
。虽然您仍然需要 bridge ,因为您无法修改库,但为了完整答案,我提到了这一点。