使用Qt线程的最小示例
Minimal Example to use Qt Threads?
TL;博士:
我正在寻找一个简洁的Qt线程示例,我发现的只是一个复杂的"养眼药",它声称可以展示QThread是如何工作的,但它们太多了,无法理解。
我接受了Zen的回答,因为他向我展示了我的尝试所遗漏的确切内容,然后添加了我自己的答案,成为我想看到的例子。单击此处跳过:https://stackoverflow.com/a/34561122/3491308
我最初的问题如下:
似乎我一定错过了什么,但我似乎无法使它像我认为的那样工作。我的完整应用程序需要有两到三个线程,所有这些线程(几乎)一起启动并永远运行:
- GUI
- 在启动其他两个线程处理对象之前,先从文件中加载一堆对象
- 实时处理器
- 具有TimeCriticalPriority和周期性的QTimer"中断",试图将PC用作比我开始使用的功能强大得多的嵌入式系统。这个项目一开始是完全嵌入的,很快就变得过于复杂,无法在那里进行管理
- USB HID驱动程序
- 连接到项目中仍然嵌入的部分
当然,核心功能在实时处理线程中,另外两个只是调整它的工作方式。因此,在实时处理器运行时,根据用户在GUI中的操作,会创建、修改和销毁处理对象,其中一些对象中的数据也会由USB产生和消耗。
我一直在寻找如何在Qt中使用线程的例子,但我一直得到复杂的"养眼药"类型的应用程序,这些应用程序混淆了线程部分。我尽我最大的努力解释它们,并写下我自己的,但我在Qt本身中不断出现segfault,我的小"游乐场"项目说我甚至没有第二个线程:
#ifndef MAIN_H
#define MAIN_H
#include <QtWidgets>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent = 0);
~MainWindow() {}
private:
QObject* mythingy;
private slots:
void deleteObject(QObject* thingy);
};
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QObject* thingy, QObject* parent = 0);
private:
QObject* mythingy;
signals:
void deleteObject(QObject* thingy);
private slots:
void doWork();
};
#endif // MAIN_H
/***************
*** main.cpp ***
***************/
#include "main.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
mythingy = new QObject(this);
QThread* thisthread = this->thread();
QThread* mainthread = QCoreApplication::instance()->thread();
//breakpoint here to check thisthread and mainthread
Worker* worker = new Worker(mythingy, this);
connect(worker, SIGNAL(deleteObject(QObject*)), this, SLOT(deleteObject(QObject*)));
}
void MainWindow::deleteObject(QObject* thingy)
{
QThread* thisthread = this->thread();
QThread* mainthread = QCoreApplication::instance()->thread();
//breakpoint here to check thisthread and mainthread
delete thingy;
}
Worker::Worker(QObject* thingy, QObject* parent)
: QObject(parent)
{
mythingy = thingy;
QThread* thread = new QThread(this);
this->moveToThread(thread);
//use a timer to allow the constructor to exit
QTimer* timer = new QTimer(this);
timer->setSingleShot(true);
timer->start(1000);
connect(timer, SIGNAL(timeout()), this, SLOT(doWork()));
QThread* thisthread = this->thread();
QThread* mainthread = QCoreApplication::instance()->thread();
//breakpoint here to check thisthread and mainthread
thread->start();
}
void Worker::doWork()
{
QThread* thisthread = this->thread();
QThread* mainthread = QCoreApplication::instance()->thread();
//breakpoint here to check thisthread and mainthread
deleteObject(mythingy);
}
如果有人能发布一个如何正确处理QThreads的例子,考虑到我对项目的描述,并且尽可能少地混乱(如果可能的话,最好比我的更短),我会非常感激
编辑:
根据Zen的回答,这似乎也有效:
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
mythingy = new QObject(this);
QThread* thisthread = this->thread();
QThread* mainthread = QCoreApplication::instance()->thread();
Worker* worker = new Worker(mythingy, this);
}
Worker::Worker(QObject* thingy, QObject* parent)
: QObject(0) //no real parent, so we can move to a different thread
{
mythingy = thingy;
QTimer* timer = new QTimer(this);
timer->setSingleShot(true);
timer->start(1000);
connect(timer, SIGNAL(timeout()), this, SLOT(doWork()));
QThread* thread = new QThread(parent);
this->moveToThread(thread);
thread->start();
}
void Worker::doWork()
{
QThread* thisthread = this->thread();
QThread* mainthread = QCoreApplication::instance()->thread();
delete mythingy;
}
仍然在构造函数中移动自己,现在直接删除对象,而不是告诉所有者的线程去做
有什么不对的吗?
这是我在Zen的帮助下寻找的简明示例。如果你一直向下滚动它,它很适合(至少在我的屏幕上)。顶部的头文件只是为了使其编译。
#ifndef MAIN_H
#define MAIN_H
#include <QtWidgets>
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QObject* thingy, QObject* parent = 0);
private:
QObject* mythingy;
private slots:
void doWork();
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget* parent = 0);
~MainWindow();
private:
QObject* mythingy;
Worker* myworker;
};
#endif // MAIN_H
/***************
*** main.cpp ***
***************/
#include "main.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
mythingy = new QObject(this);
myworker = new Worker(mythingy, this);
}
MainWindow::~MainWindow()
{
delete myworker;
}
Worker::Worker(QObject* thingy, QObject* parent)
: QObject(0) //no real parent, so we can move to a different thread
{
mythingy = thingy;
//move myself to a new thread and start it
QThread* thread = new QThread(parent);
connect(thread, SIGNAL(started()), this, SLOT(doWork()));
this->moveToThread(thread);
thread->start();
}
void Worker::doWork()
{
//deleting an object from a different thread:
//requires careful planning to make it safe, but seems to work as expected
delete mythingy;
}
评论指出了重要的部分,其余的部分也只是为了让它运行起来。您可以将其添加到doWork()中,以验证其是否为不同的线程:
QThread* thisthread = this->thread();
QThread* mainthread = QCoreApplication::instance()->thread();
在此之后立即设置断点,并查看指针是否不同。
至于segfault,当我做对了之后,基于线程的segfault似乎就消失了,只留下了天真的、非线程安全的代码来过早地删除内容。
1。不能使用父对象移动对象。因此,应该使用Worker* worker = new Worker(mythingy);
来代替Worker* worker = new Worker(mythingy, this);
2.不能为处于不同线程中的父级创建子级。
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent)
{
mythingy = new QObject(this);
QThread* thisthread = this->thread();
QThread* mainthread = QCoreApplication::instance()->thread();
//breakpoint here to check thisthread and mainthread
//*****************
Worker* worker = new Worker(mythingy);
QThread* thread = new QThread();
worker->moveToThread(thread);
thread->start();
//*****************
connect(worker, SIGNAL(deleteObject(QObject*)), this, SLOT(deleteObject(QObject*)));
}
Worker::Worker(QObject* thingy, QObject* parent)
: QObject(parent)
{
mythingy = thingy;
// QThread* thread = new QThread(this);
// this->moveToThread(thread);
//use a timer to allow the constructor to exit
QTimer* timer = new QTimer(this);
timer->setSingleShot(true);
timer->start(1000);
connect(timer, SIGNAL(timeout()), this, SLOT(doWork()));
// QThread* thisthread = this->thread();
// QThread* mainthread = QCoreApplication::instance()->thread();
//breakpoint here to check thisthread and mainthread
// thread->start();
}
对象的槽总是由它所在的线程执行。因此,由于您从未移动过MainWindow
,因此不需要在MainWindow::deleteObject
中检查它的线程。例如:
Worker* worker = new Worker(mythingy);
QThread* thread = new QThread();
worker->moveToThread(thread);
thread->start();
//wrong: directly invoking doWork in mainthread
worker->doWork();
//correct: through signal-slot mechanics
connect(this, SIGNAL(startWork()), worker, SLOT(doWork()));
- 我可以使用Qt线程ID为每个线程创建唯一的缓存吗?
- Qt 线程两个参数
- 从QTcpSocket上的数据流中连续运行复杂算法的最佳Qt线程解决方案是什么
- QT 5:从非QT线程发射信号
- 从另一个(非 qt)线程调用 QObject 方法的线程安全性?
- 无法在线程运行方法中运行CryptoPP FileSink的情况下终止Qt线程
- QT线程中的Lua脚本
- 显示Q对话框时出现QT线程问题
- Qt线程设计生产者消费者
- 在退出应用程序时安全退出Qt线程
- Qt 线程等待来自 GUI 的输入
- 在Qt线程之间发送大量数据
- 带有 movetothread的 qt 线程
- 在Qt线程中应用OpengGL着色器
- Qt线程同步设计
- Qt 线程,来自 QObject 的析构函数的崩溃移动
- 使用 QT 线程解决运行时问题
- 使用Qt线程和信号的缓冲区溢出
- Qt线程在内核上的共享似乎并不均等
- 使用Qt线程的最小示例