带有 Qt 事件循环的便携式通用共享库设置
Portable generic shared library setup with Qt event loop
我们正在尝试编写一个可移植的共享库,为了方便起见,它使用了一些 Qt classes(主要是 QTimer
和 QTcpSocket
);不过,没有 GUI 的东西。相应的 signal/slot 连接似乎需要一些 Qt 事件循环,因此我们 "prime" a QCoreApplication
如 中所述。因此,我们设置了一个工作对象来完成繁重的工作并将其移动到 QThread
.
我们 运行 现在遇到的问题是 QThread
的所有者对象(在主线程中)和 QThread
中的工作对象之间的排队连接似乎永远不会在 Linux 系统上得到处理,至少只要实现我们的库的程序不在主线程中提供它自己的任何进一步的 Qt 事件循环。这不是很有帮助,因为从 worker 传递到主线程的数据应该使用一些回调函数进一步传递,但现在永远不会被调用。
因此我的问题是:有没有一种方法可以让事件循环在库主线程中工作而不锁定它或主机程序(这似乎只是放置 QCoreApplication::exec()
或类似的地方)?或者我们是否必须设置不同的线程间通信方案(独立于 Qt)以处理这些数据传输?
由于我们不知道主机软件是否会在 QApplication
上 运行,理想情况下我也会在设置主线程事件循环之前进行检查.一个简单的 if(qApp != nullptr)
就够了吗?
P.S.: 一些我尝试过但对我不起作用的事情,要么:
- 在从主线程启动的
std::thread
中设置 QEventLoop
(可能不工作,因为仍然不在主线程中)
- 在主线程 class 中设置一个
QEventLoop
并使用 QTimer
定期触发其 processEvents()
函数(可能由于缺少事件循环而无法工作主函数中的 QTimer::timeout
信号)
- 在
std::thread
中启动 QCoreApplication
(在 Windows 上发出 运行 次警告,QCoreApplication 应在主线程中启动)
在 Qt 中,回调称为 Qt::DirectConnection
。但是当然,这些回调将 运行 在您的工作线程上。但是任何其他使用回调的库都是这种情况,所以 Qt 在这里不是问题,你的代码也不是:基本思想有这个 属性。
如果主机应用程序没有使用事件循环(任何事件循环,不一定是 Qt 的),那么除了轮询之外你无能为力——见下文。
如果主机应用程序运行是一个 X11 事件循环,那么您需要确保您的 Qt 副本使用与主机应用程序相同的底层事件循环。通常,这将是 glib 的事件循环,然后它应该自动运行。否则,您需要将 Qt 事件循环使用的同步原语的文件描述符传递给用户,用户需要将其集成到他们的事件循环中。无论您是否使用 Qt,您都会面临同样的问题:滚动您自己的通信方法不会解决它,因为您仍然需要一个可等待的原语,它将与用户正在使用的任何事件循环进行互操作。
用户当然可以随时轮询回调:公开一个 mainPoll()
转发到 QCoreApplication::processEvents()
的方法。
尽管接受了另一个答案(我认为这个答案更正确),但我仍然想提一个非常有效的解决方法:我们实际上设法通过连接解决了大多数系统上的事件 loop/thread 问题工作线程在设置工作线程的 class 的构造函数中使用 lambda 函数发出信号。
现在,我怀疑这种行为是否是线程安全的,并且在 connect
函数调用中声明相对冗长的 lambda 函数肯定不是好的风格。但如果其他人最终遇到这个问题,这可能是一个短期解决方案或(临时)解决方法。
我们正在尝试编写一个可移植的共享库,为了方便起见,它使用了一些 Qt classes(主要是 QTimer
和 QTcpSocket
);不过,没有 GUI 的东西。相应的 signal/slot 连接似乎需要一些 Qt 事件循环,因此我们 "prime" a QCoreApplication
如 QThread
.
我们 运行 现在遇到的问题是 QThread
的所有者对象(在主线程中)和 QThread
中的工作对象之间的排队连接似乎永远不会在 Linux 系统上得到处理,至少只要实现我们的库的程序不在主线程中提供它自己的任何进一步的 Qt 事件循环。这不是很有帮助,因为从 worker 传递到主线程的数据应该使用一些回调函数进一步传递,但现在永远不会被调用。
因此我的问题是:有没有一种方法可以让事件循环在库主线程中工作而不锁定它或主机程序(这似乎只是放置 QCoreApplication::exec()
或类似的地方)?或者我们是否必须设置不同的线程间通信方案(独立于 Qt)以处理这些数据传输?
由于我们不知道主机软件是否会在 QApplication
上 运行,理想情况下我也会在设置主线程事件循环之前进行检查.一个简单的 if(qApp != nullptr)
就够了吗?
P.S.: 一些我尝试过但对我不起作用的事情,要么:
- 在从主线程启动的
std::thread
中设置QEventLoop
(可能不工作,因为仍然不在主线程中) - 在主线程 class 中设置一个
QEventLoop
并使用QTimer
定期触发其processEvents()
函数(可能由于缺少事件循环而无法工作主函数中的QTimer::timeout
信号) - 在
std::thread
中启动QCoreApplication
(在 Windows 上发出 运行 次警告,QCoreApplication 应在主线程中启动)
在 Qt 中,回调称为 Qt::DirectConnection
。但是当然,这些回调将 运行 在您的工作线程上。但是任何其他使用回调的库都是这种情况,所以 Qt 在这里不是问题,你的代码也不是:基本思想有这个 属性。
如果主机应用程序没有使用事件循环(任何事件循环,不一定是 Qt 的),那么除了轮询之外你无能为力——见下文。
如果主机应用程序运行是一个 X11 事件循环,那么您需要确保您的 Qt 副本使用与主机应用程序相同的底层事件循环。通常,这将是 glib 的事件循环,然后它应该自动运行。否则,您需要将 Qt 事件循环使用的同步原语的文件描述符传递给用户,用户需要将其集成到他们的事件循环中。无论您是否使用 Qt,您都会面临同样的问题:滚动您自己的通信方法不会解决它,因为您仍然需要一个可等待的原语,它将与用户正在使用的任何事件循环进行互操作。
用户当然可以随时轮询回调:公开一个 mainPoll()
转发到 QCoreApplication::processEvents()
的方法。
尽管接受了另一个答案(我认为这个答案更正确),但我仍然想提一个非常有效的解决方法:我们实际上设法通过连接解决了大多数系统上的事件 loop/thread 问题工作线程在设置工作线程的 class 的构造函数中使用 lambda 函数发出信号。
现在,我怀疑这种行为是否是线程安全的,并且在 connect
函数调用中声明相对冗长的 lambda 函数肯定不是好的风格。但如果其他人最终遇到这个问题,这可能是一个短期解决方案或(临时)解决方法。