【发布时间】:2015-11-15 10:21:41
【问题描述】:
我想用我的代码实现几件事:
两个线程,同时向控制台打印“foo”和“bar”100 次。顺序无关紧要。
创建线程的对象在完成时需要获得一个信号。在现实生活中,这个信号会携带线程的结果,创建者需要等待这个结果。
这是我为此示例编写的代码:
main.cpp
#include <QCoreApplication>
#include "controller.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Controller controller;
return a.exec();
}
控制器.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include <QThread>
#include <QEventLoop>
#include <QTimer>
#include "textwriter.h"
class Controller: public QObject
{
Q_OBJECT
public:
Controller(QObject* parent = 0);
signals:
void startWrite();
public slots:
void catchFinish();
};
#endif // CONTROLLER_H
controller.cpp
#include "controller.h"
Controller::Controller(QObject* parent):QObject(parent)
{
// QThread
QThread threadA;
QThread threadB;
// Worker that will send to the thread
textWriter workerA("foo");
textWriter workerB("bar");
// Send the worker to the thread
workerA.moveToThread(&threadA);
workerB.moveToThread(&threadB);
// I will emit startWrite() later to begin all the job
connect(this, &Controller::startWrite, &workerA, &textWriter::write);
connect(this, &Controller::startWrite, &workerB, &textWriter::write);
// The worket will send back finish signal to me
connect(&workerA, &textWriter::finish, this, &Controller::catchFinish);
connect(&workerB, &textWriter::finish, this, &Controller::catchFinish);
// Also kill the thread as well
connect(&workerA, &textWriter::finish, &threadA, &QThread::quit);
connect(&workerB, &textWriter::finish, &threadB, &QThread::quit);
// If the thread was killed, let the system delete it
connect(&threadA, &QThread::finished, &threadA, &QThread::deleteLater);
connect(&threadB, &QThread::finished, &threadB, &QThread::deleteLater);
// I start the thread here
emit startWrite();
QEventLoop eventloopA;
connect(&workerA, &textWriter::finish, &eventloopA, &QEventLoop::quit);
QEventLoop eventloopB;
connect(&workerB, &textWriter::finish, &eventloopB, &QEventLoop::quit);
threadA.start();
threadB.start();
eventloopA.exec();
eventloopB.exec();
}
void Controller::catchFinish()
{
qDebug() << "finish signal catched";
}
textwriter.h
#ifndef TEXTWRITER_H
#define TEXTWRITER_H
#include <QObject>
#include <QThread>
#include <QDebug>
class textWriter : public QObject
{
Q_OBJECT
QString text;
public:
textWriter(QString text, QObject *parent = 0);
public slots:
void write();
signals:
void finish();
};
#endif // TEXTWRITER_H
textwriter.cpp
#include "textwriter.h"
textWriter::textWriter(QString text,QObject* parent):QObject(parent)
{
this->text = text;
}
void textWriter::write()
{
for (int i = 0; i < 100; ++i)
{
qDebug() << "Thread Number:\t"
<< QThread::currentThreadId()
<< "\tCount: "
<< i
<< "\t"
<< text;
}
emit finish();
qDebug() << "finish() signal emitted";
}
但是我得到了所有类型的结果。 我想知道这些结果的原因。
两个线程都按预期工作。他们依次做事并完成。
理由:没有理由。这是所需的操作。一个线程首先从 0 转到 99,然后第二个线程开始。程序完成。
原因:由cpu调度器决定。两个线程同时启动。他们做他们的工作。但是,当第一个线程完成时,程序终止。第二个线程的最后一次打印仍然是 count = 38。
原因:?两个线程都各司其职。但是控制台中有一个
Error: double free or corruption。
原因:?QThread 在打印运行时销毁。
原因: ?似乎与问题 3 的原因相同。
最新更新和建议:
我在这里发表一些评论,以帮助像我一样难以理解QThread 的人。这是由在这些问题main-thread-cannot-get-signal-form-worker-thread、qt-code-sequence-in-multitread 和当前帖子中帮助我的人贡献的。另外,这个article 是一本非常好的读物。
首先你需要创建一个继承QObject的对象。这个对象是多线程作业运行的地方。如article 中所述,对象构造函数不应包含使用new 在堆中创建的任何内容。构造函数对象最好尽可能简单,因为作业不应该在构造函数中完成。相反,所有作业都应该放在这个对象的槽中。
创建finish() 信号也很重要。这个信号应该在每个作业函数结束时发出,以便告诉线程控制器它已经完成了它的工作。这也是将工作结果放入完成信号中的好方法,例如finish(int, QString,.......)
这是工作对象的样子:
class Object : public QObject
{
Q_OBJECT
public:
Object(QObject *parent = 0);
// All multi thread works should create in term of slot
public slots:
void job1();
void job2();
void job3();
void job4();
signals:
void finish();
void finish(int result);
};
首先,我们创建这个对象和一个QThread 对象。注意QThread 对象的创建方式,在堆中还是在堆栈中。它将导致两种不同的方法来结束线程。
QThread stackThread;
Object workerStack;
QThread* heapThread = new Qthread;
Object workerHeap;
创建线程和worker对象后,只需将worker移动到线程中即可。这里没有魔法。
workerStack.moveToThread(&stackThread);
workerHeap.moveToThread(heapThread);
然后连接一个信号,这样当QThread::start被调用时,对象就开始工作了:
connect(&stackThread, &QThread::started, &workerStack, &Object::job1);
connect(heapThread, &QThread::started, &workerHeap, &Object::job2);
您可以只使用finish() 信号来停止线程。
connect(&workerStack, &Object::finish, &stackThread, &QThread::quit);
connect(&workerHeap, &Object::finish, heapThread, &QThread::quit);
您可能需要多一个信号槽,以便在堆中创建的线程在完成后可以自行删除。这对于在堆栈中创建的线程来说是不需要的。
connect(&workerHeap, &Object::finish, heapThread, &QThread::deleteLater);
如果你的finish() 信号包含返回结果,你可以这样传达你的结果。
connect(&workerStack, &Object::finish, this, &THISSCOPE::catchFinish);
如果你需要等待线程结果继续你的程序,你可以使用QEventLoop。请不要使用不应执行此工作的 QThread::wait 函数。 wait 函数只是阻塞当前的事件循环,这样你就永远无法捕捉到finish 信号。
虽然使用QEventLoop可以让你接收到finish信号,但这并不是一个好的设计。有一些方法可以避免它,但我不会在这里详细说明。我只是用QEventLoop来演示当前示波器如何接收信号。代码如下:
QEventLoop eventloop;
connect(&workerStack, &Object::finish, &eventloop, &QEventLoop::quit);
现在,我们可以在所有 slot-slot 创建后启动线程:
threadStack.start();
threadHeap.start();
如果需要等待结果,则启动事件循环:
eventloop.exec();
当线程完成时,在堆中创建的线程应该发出finish 信号并删除自己(因为您连接到它的deleteLater 插槽)。但是,在堆栈上创建的线程有点棘手。如果您的主代码超出此处的范围,您可能会收到错误“QThread 在运行时被破坏”。这是因为主代码比QThread::quit 快一点。因此,您需要在此处输入QThread::wait。 wait 函数的真正用途是等待线程在作业完成后退出。 这不是等待工作完成。所以最后,我们添加这个:
threadStack.wait();
【问题讨论】:
标签: c++ multithreading qt