Multithreading 如何在从非Qt多线程库调用的QObject派生类中实现回调函数? (伪)码
以下是我遇到问题的概念的不可编译代码草图:Multithreading 如何在从非Qt多线程库调用的QObject派生类中实现回调函数? (伪)码,multithreading,qt,callback,Multithreading,Qt,Callback,以下是我遇到问题的概念的不可编译代码草图: struct Data {}; struct A {}; struct B {}; struct C {}; /* and many many more...*/ template<typename T> class Listener { public: Listener(MyObject* worker):worker(worker) { /* do some magic to register with RTI DDS *
struct Data {};
struct A {};
struct B {};
struct C {};
/* and many many more...*/
template<typename T>
class Listener {
public:
Listener(MyObject* worker):worker(worker)
{ /* do some magic to register with RTI DDS */ };
public:
// This function is used ass a callback from RTI DDS, i.e. it will be
// called from other threads when new Data is available
void callBackFunction(Data d)
{
T t = extractFromData(d);
// Option 1: direct function call
// works somewhat, but shows "QObject::startTimer: timers cannot be started
// from another thread" at the console...
worker->doSomeWorkWithData(t); //
// Option 2: Use invokeMethod:
// seems to fail, as the macro expands including '"T"' and that type isn't
// registered with the QMetaType system...
// QMetaObject::invokeMethod(worker,"doSomeGraphicsWork",Qt::AutoConnection,
// Q_ARG(T, t)
// );
// Option 3: use signals slots
// fails as I can't make Listener, a template class, a QObject...
// emit workNeedsToBeDone(t);
}
private:
MyObject* worker;
T extractFromData(Data d){ return T(d);};
};
class MyObject : public QObject {
Q_OBJECT
public Q_SLOTS:
void doSomeWorkWithData(A a); // This one affects some QGraphicsItems.
void doSomeWorkWithData(B b){};
void doSomeWorkWithData(C c){};
public:
MyObject():QObject(nullptr){};
void init()
{
// listeners are not created in the constructor, but they should have the
// same thread affinity as the MyObject instance that creates them...
// (which in this example--and in my actual code--would be the main GUI
// thread...)
new Listener<A>(this);
new Listener<B>(this);
new Listener<C>(this);
};
};
main()
{
QApplication app;
/* plenty of stuff to set up RTI DDS and other things... */
auto myObject = new MyObject();
/* stuff resulting in the need to separate "construction" and "initialization" */
myObject.init();
return app.exec();
};
一些相关的想法:
- 我曾考虑在
语句之前引入if
,但我决定不这样做:函数中唯一可能并行使用的部分(在QMutexLocker
语句中if
以上的部分)是线程安全的return;
- 手动将连接类型设置为
:从技术上讲,如果我理解正确,Qt应该做正确的事情,默认的Qt::QueuedConnection
,应该最终成为Qt::AutoConnection
。但是,由于当这个声明达成时,情况总是如此,我决定明确地把它放在那里,以提醒自己为什么会有这样的情况Qt::QueuedConnection
- 将排队代码直接放在函数中,而不是隐藏在临时函数中:我可以选择将对
的调用放在另一个临时函数中,比如invokeMethod
queueDoSomeWorkWithData(),它将由侦听器中的回调调用,然后使用
invokeMethod
和
上的doSomeWorkWithData()
Qt::AutoConnection'。我决定不这样做,因为我似乎没有办法通过模板自动编码这个临时函数(模板和元系统是原始问题的一部分),所以我的代码的“用户”(即实现
)也必须手动键入临时函数(这就是模板化类型名称正确解析的方式)。在我看来,将签入实际函数似乎可以安全地键入额外的函数头,使doSomeWorkWithData(XYZ XYZ)
界面更干净,并更好地提醒读者MyClass
可能有一个线程问题潜伏在黑暗中doSomeWorkWithData()
doSomeWorkWithData
函数创建一个threadSafeDoSomeWorkWithData
函数,该函数只执行QMetaMethod::invoke
非线程安全函数
public:
void threadSafeDoSomeWorkWithData(A a) {
QMetaMethod::invoke("doSomeWorkWithData", Q_ARG(A,a));
}
Q_INVOKABLE void doSomeWorkWithData(A a);
或者,Sergey Tachenov提出了一种有趣的方法,可以在中做或多或少相同的事情。他将我建议的两个功能合并为一个
void Obj2::ping() {
if (QThread::currentThread() != this->thread()) {
// not sure how efficient it is
QMetaObject::invoke(this, "ping", Qt::QueuedConnection);
return;
}
// thread unsafe code goes here
}
至于为什么不创建GUI时会出现正常行为?除了操作GUI对象之外,可能你没有做任何其他不安全的事情。或者,它们可能是线程安全问题显而易见的唯一地方。你能否澄清标记为
public Q_SIGNALS:
的部分是否应该是public:
或QSIGNALS:
?是doSomeWorkWithData(A)
直接操纵QGraphicsItems,还是其他对象捕捉到的信号起作用?@Slavik81:嗯,实际上应该是public Q\u QSLOTS:
…我编辑了这个示例。抱歉混淆。Wrt.图形项目:doSomeWorkWithData(A)
影响图形项目,因为a
包含有关(自定义)项目的形状和位置的信息。doSomeWorkWithData(a)
使用项目setPos()
方法更新位置,并使用其他方法更改形状。(项目内部应该做正确的事情,即使用prepareGeometryChange()
,等等)谢谢你的这个想法和链接。我目前正在尝试通过检查QThread::currentThread()!=this->thread()
(我没有意识到我可以这么做)和通过QMetObject::invokeMethod()排队等待调用来解决问题。我将根据结果更新我的问题(也就是说,我也将推迟接受答复)。
void Obj2::ping() {
if (QThread::currentThread() != this->thread()) {
// not sure how efficient it is
QMetaObject::invoke(this, "ping", Qt::QueuedConnection);
return;
}
// thread unsafe code goes here
}