我认为你做得太过分了。 Qt 的美妙之处在于您尝试做的事情非常简单。
你似乎认为QTimer
会以某种方式神奇地中断你正在运行的线程来执行回调。在 Qt 中从来没有出现过这种情况。在 Qt 中,所有事件和信号都传递到线程中运行的事件循环,并且该事件循环将它们分派到 QObject。因此,在线程内,由于 Qt 的事件和信号/槽框架,不存在并发危险。此外,只要您依赖 Qt 的信号槽和事件机制在线程之间进行通信,您就不需要使用任何其他访问控制原语,因为每个线程内的事物都是序列化的。
因此,您的问题是您从未在线程中运行事件循环,因此永远不会拾取超时事件并将其分派到您连接到的插槽timeout()
signal.
A QTimer
可以在任何线程中创建和启动,只要启动它的线程是计时器所在的线程QObject
QObject 属于您在其中创建它们的线程,除非您使用以下命令将它们移动到不同的线程QObject::moveToThread(QThread*)
.
指某东西的用途invokeMethod
启动定时器是完全没有必要的。毕竟,您是从计时器所在的同一个线程启动计时器的。顾名思义,排队信号槽连接会将信号排队到队列中。具体来说,当您发出信号时,QMetaCallEvent
为接收器槽所在的 QObject 排队。事件循环必须在槽对象的线程中运行以获取该对象并执行调用。您永远不会在线程中调用任何内容来清空该队列,因此没有什么可以调用您的timeout()
slot.
至于静态成员初始化惨败,您可以自由地构建整个 T 对象main()
或者在主线程中的 QObject 中,然后将其移动到新线程 - 这就是不使用 QtConcurrent 时的做法。使用 QtConcurrent 时,您的可运行函数可以构造和销毁任意数量的 QObject。
要修复您的代码,lambda 应该旋转一个事件循环,因此:
[] () {
sapp s;
s.f();
QEventLoop l;
l.exec();
}
下面我附上了一个 SSCCE 示例,说明如何在 Qt 中惯用地完成它。如果不想使用 QtConcurrent,那么会有两个更改:您想要qApp->exit()
就在你之后exit()
线程的事件循环——否则,a.exec()
永远不会退出(它怎么知道退出?)。你也想wait()
在退出之前在线程上main()
功能 - 破坏是不好的风格QThreads
仍在运行。
The exit()
函数仅指示事件循环完成,将它们视为设置标志,而不是真正自行退出任何内容 - 因此它们与 C 风格的 API 不同exit()s
.
请注意,一个QTimer
is a QObject
在其本身。出于性能原因,如果计时器经常触发,那么使用QBasicTimer
这是一个简单的包装器返回的计时器IDQObject::startTimer()
。然后你会重新实现QObject::timerEvent()
。这样就可以避免信号槽调用的开销。根据经验,直接(非排队)信号槽调用的成本大约与将两个 1000 个字符的 QString 连接在一起的成本相同。看我的基准 https://stackoverflow.com/questions/10838013/large-use-of-signals-and-slots-does-affect-application-performance/10839370#10839370.
旁注:如果您要发布简短的示例,直接发布整个代码可能会更容易,这样将来就不会丢失——只要它们都在一个文件中。将 100 行示例代码分散在三个文件中会适得其反。看下面,关键是#include "filename.moc"
在......的最后filename.cpp
。它还有助于以 Java 风格一次性定义和声明方法。所有这些都是为了保持简短且易于理解。毕竟,这只是一个例子。
//main.cpp
#include <QtCore/QTimer>
#include <QtCore/QDebug>
#include <QtCore>
#include <QtCore/QCoreApplication>
class Class : public QObject
{
Q_OBJECT
QTimer timer;
int n;
private slots:
void timeout() {
qDebug() << "hi";
if (! --n) {
QThread::currentThread()->exit();
}
}
public:
Class() : n(5) {
connect(&timer, SIGNAL(timeout()), SLOT(timeout()));
timer.start(500);
}
};
void fun()
{
Class c;
QEventLoop loop;
loop.exec();
qApp->exit();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QtConcurrent::run(&fun);
return a.exec();
}
#include "main.moc"