如何在另一个线程中函数返回之前阻塞线程?

时间:2017-09-28 08:39:30

标签: c++ multithreading qt c++11 thread-safety

如果我有一个主线程和一个单独的永久工作者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列表的互斥锁无法正常工作,因为它们需要插入第三方库中。

1 个答案:

答案 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 ,因为您无法修改库,但为了完整答案,我提到了这一点。