计时器是否从另一个线程启动?
Is the timer being started from another thread?
QThread 文档建议了两种在单独的线程中制作代码 运行 的方法。如果我将 QThread 子类化并重新实现 运行(),那么我会得到
QBasicTimer::start: Timers cannot be started from another thread
-
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QThread
{
Q_OBJECT
int id;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == id) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
qDebug() << QThread::currentThreadId();
QBasicTimer timer;
id = timer.timerId();
timer.start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
qDebug() << QThread::currentThreadId();
worker = new Worker;
worker->start();
}
~MainWidget(){worker->abort();}
};
1) 计时器是否从另一个线程启动?
2) 当 QBasicTimer 被 QTimer 替换时,为什么我没有收到警告?
3) 为什么我在使用 moveToThread 时没有收到警告?
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QObject
{
Q_OBJECT
QBasicTimer* timer;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == timer->timerId()) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
timer = new QBasicTimer;
timer->start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
~MainWidget(){worker->abort(); thread.quit(); thread.wait();}
signals:
void start();
};
关于第一个(非moveToThread
)示例...
快速查看 QBasicTimer::start
的 Qt 源代码显示如下...
void QBasicTimer::start(int msec, QObject *obj)
{
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
// ...
if (Q_UNLIKELY(obj && obj->thread() != eventDispatcher->thread())) {
qWarning("QBasicTimer::start: Timers cannot be started from another thread");
return;
}
因此它期望它的第二个参数 obj
具有等于当前线程的线程亲和力。
但是,在您的 Worker::run
实施中,您有...
timer.start(1000, this);
在此上下文中,当前线程是由 QThread
实例创建的新线程,但 this
指的是 MainWidget
在主线程上创建的 QWorker
实例图形用户界面线程。因此警告。
编辑 1:
对于问题...
why it works with moveToThread()?
考虑 MainWidget
构造函数的实现...
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
在调用 Worker::run
时,Worker
实例已移至新线程。所以当线...
timer.start(1000, this);
执行,this
(指的是 Worker
实例)在当前线程上并且 QBasicTimer::start
中的线程亲和性测试在没有警告的情况下通过。
抱歉,如果上面的内容有点令人费解,但重要的是要考虑第二个 arg 与 QBasicTimer::start
的线程关联:它 必须 是当前 运行线程。
QThread 文档建议了两种在单独的线程中制作代码 运行 的方法。如果我将 QThread 子类化并重新实现 运行(),那么我会得到
QBasicTimer::start: Timers cannot be started from another thread
-
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QThread
{
Q_OBJECT
int id;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == id) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
qDebug() << QThread::currentThreadId();
QBasicTimer timer;
id = timer.timerId();
timer.start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
qDebug() << QThread::currentThreadId();
worker = new Worker;
worker->start();
}
~MainWidget(){worker->abort();}
};
1) 计时器是否从另一个线程启动?
2) 当 QBasicTimer 被 QTimer 替换时,为什么我没有收到警告?
3) 为什么我在使用 moveToThread 时没有收到警告?
#include <QWidget>
#include <QThread>
#include <QBasicTimer>
#include <QDebug>
#include <QEvent>
#include <QCoreApplication>
class Worker : public QObject
{
Q_OBJECT
QBasicTimer* timer;
bool m_abort = false;
bool compute = false;
public:
Worker() {}
protected:
void timerEvent(QTimerEvent *event) override {
if (event->timerId() == timer->timerId()) {
compute = true;
} else {
QObject::timerEvent(event);
}
}
public slots:
void abort() {m_abort = true;}
void run() {
timer = new QBasicTimer;
timer->start(1000, this);
forever {
if (m_abort) break;
QCoreApplication::processEvents();
if (compute)
qDebug() << "computed";
compute = false;
}
}
};
class MainWidget : public QWidget
{
Q_OBJECT
QThread thread;
Worker* worker;
public:
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
~MainWidget(){worker->abort(); thread.quit(); thread.wait();}
signals:
void start();
};
关于第一个(非moveToThread
)示例...
快速查看 QBasicTimer::start
的 Qt 源代码显示如下...
void QBasicTimer::start(int msec, QObject *obj)
{
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
// ...
if (Q_UNLIKELY(obj && obj->thread() != eventDispatcher->thread())) {
qWarning("QBasicTimer::start: Timers cannot be started from another thread");
return;
}
因此它期望它的第二个参数 obj
具有等于当前线程的线程亲和力。
但是,在您的 Worker::run
实施中,您有...
timer.start(1000, this);
在此上下文中,当前线程是由 QThread
实例创建的新线程,但 this
指的是 MainWidget
在主线程上创建的 QWorker
实例图形用户界面线程。因此警告。
编辑 1:
对于问题...
why it works with moveToThread()?
考虑 MainWidget
构造函数的实现...
MainWidget()
{
worker = new Worker;
worker->moveToThread(&thread);
connect(this, &MainWidget::start, worker, &Worker::run);
thread.start();
emit start();
}
在调用 Worker::run
时,Worker
实例已移至新线程。所以当线...
timer.start(1000, this);
执行,this
(指的是 Worker
实例)在当前线程上并且 QBasicTimer::start
中的线程亲和性测试在没有警告的情况下通过。
抱歉,如果上面的内容有点令人费解,但重要的是要考虑第二个 arg 与 QBasicTimer::start
的线程关联:它 必须 是当前 运行线程。