gpt4 book ai didi

c++ - 如何阻塞线程直到一个函数在另一个线程中返回?

转载 作者:行者123 更新时间:2023-11-30 01:39:08 24 4
gpt4 key购买 nike

如果我有一个主线程和一个单独的永久工作线程 QThread:

// Main thread where the event loop and QCoreApplication runs.
class NetworkController : public QObject {
public:
Q_OBJECT

void connect_to_ap()
{
WifiAP *myAP = netMgr.get_best_ap();

myAP->connect("my_psk_password");
}

// This is a class from a 3rd-party library, which contains, owns
// and manages the lifetime of "WifiAP" instances.
NetworkManager netMgr;
};

// Separate thread where a state machine runs.
class StateMachineWorker : public QObject {
public:
Q_OBJECT

void on_ready_to_connect_event()
{
// HERE: How to trigger NetworkController::connect_to_ap() and
// *block* until it returns.
}

NetworkController *pNetCtrlr;
}

当状态机类进入某个状态时,它应该连接到一个 AP(接入点)。 NetworkController具有连接到 AP 的功能。

我正在尝试找出一种方法,让状态机能够以线程安全的方式执行此操作。问题是NetworkManager始终更新其 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()执行完毕;这绝不能发生。

互斥锁保护列表WifiAPNetworkManager将无法工作,因为它们需要插入到第 3 方库中。

最佳答案

您可以使用 QMetaObject::invokeMethod带参数 Qt::BlockingQueuedConnection .这种连接类型为您添加了所有的阻塞逻辑,您根本不必更改第三方库。

一个一般的例子:

objects.h

#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;
}
};

main.cpp

#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();
}

对应的是被调用的方法必须是槽。如果 connect_to_ap 还不是插槽,您可以创建一个桥接对象来完成这项工作,如下所述。

请记住,这个 bridge 对象必须与 NetworkController(在您的例子中是主线程)在同一个线程中,因此插槽在正确的队列中事件循环。你可以看看QObject::moveToThread了解更多信息。

快速草稿应该是这样的:

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);
}

更新

通过invokeMethod 调用方法的另一种方法是将其标记为Q_INVOKABLE。 .虽然您仍然需要 bridge 因为您无法修改库,但我提到这一点是为了回答的完整性。

关于c++ - 如何阻塞线程直到一个函数在另一个线程中返回?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46464663/

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