The QThread class provides a platform-independent way to manage threads.
A QThread object manages one thread of control within the program. QThreads begin executing in run(). By default, run() starts the event loop by calling exec() and runs a Qt event loop inside the thread.You can use worker objects by moving them to the thread using QObject::moveToThread().
The code inside the Worker’s slot would then execute in a separate thread. However, you are free to connect the Worker’s slots to any signal, from any object, in any thread. It is safe to connect signals and slots across different threads, thanks to a mechanism called queued connections.Another way to make code run in a separate thread, is to subclass QThread and reimplement run(). For example:代码1
In that example, the thread will exit after the run function has returned. There will not be any event loop running in the thread unless you call exec().
如下
QThread class是一个与平台无关的线程管理类。在程序中可以使用QThread对象来管理一个线程,QThreads对象一启动后就开始执行.run()中的代码,.run()默认情况下是通过调用exec()来开始在该线程内部执行QT事件循环(注:如果run执行返回了,那么该线程也就结束了)。此后就是介绍两种线程的实现方法了
QT提供了两种线程的实现方法,一种是继承重定义(subclass),另一种是通过QObject对象的moveToThread()方法实现,qt称之为worker-object方法
线程继承重定义
代码1
class WorkerThread : public QThread//继承线程类
{
Q_OBJECT
void run() Q_DECL_OVERRIDE {//重定义实现代码
QString result;
/* ... 线程启动后要执行的线程代码 */
emit resultReady(result);
}
signals:
void resultReady(const QString &s);
};
void MyObject::startWorkInAThread()//主线程
{
WorkerThread *workerThread = new WorkerThread(this);//实例化一个线程
//线程处理后通知主线程执行handleResults
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
//线程结束时自删对象资源
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();//开始线程
}
worker-object
代码2
class Worker : public QObject //工作对象
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... 想在在线程中实现的代码 ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;//实例化一个工作对象
worker->moveToThread(&workerThread);//提交给分线程
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);//关联不同对象间的信号与槽
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();//启动线程
}
~Controller() {
workerThread.quit();//结束线程
workerThread.wait();//等待线程结束完毕,没有这个线程退出是快了,但不安全。
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
workerThread.start()
只是启动了workerThread线程的事件循环队列(参数方法1的描述),并没有执行worker::doWork
,你需要在主线程上emit operate
才会执行哟。如果还想多几个函数在分线程上执行,你可依此定义Worker的SLOT函数和主线程中的信号关联就好,当然,在doWork
上执行循环语句时,下次再emit operate
必须等上次的执行结束才有效,同理,要结束这个线程那也必须要等doWork
循环语句执行完才能结束,所以如果用到了循环语句,必须要考虑在结束线程时如何退出循环。注意事项:
在继承重定义方法中,WorkerThread 对象的slot函数,在主线程中发出执行信号后,其执行仍是在实例化它的主线程MyObject上执行,而不是你认为的在子线程上执行。
简单举例:
class WorkerThread : public QThread
{
Q_OBJECT
void run() Q_DECL_OVERRIDE {
QString result;
qDegug()<<"执行run 函数的线程号:"<<QThread::currentThreadId(;
emit resultReady(result);
}
public slot:
void test(void){
qDegug()<<"执行slot函数的线程号:"<<QThread::currentThread();
}
signals:
void resultReady(const QString &s);
};
void MyObject::startWorkInAThread()//主线程
{
WorkerThread *workerThread = new WorkerThread(this);//实例化一个线程
//线程处理后通知主线程执行handleResults
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
//线程结束时自删对象资源
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
connect(this, SIGNAL(doTest), workerThread, &WorkerThread::test);
qDegug()<<"main线程号:"<<QThread::currentThreadId();
workerThread->start();//开始线程
emit doTest();
}
执行后可以看出SLOT所在线程是跟main线程一致的而不是与RUN所在的分线程相同。因为执行run函数时,WorkerThread是自行创建了一个新线程去调用run的。而其它函数则仍是在创建它的线程上调用的。QT说明原因如下:
a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run().This means that all of QThread’s queued slots will execute in the old thread.
还不是很明白?那再贴一段:
Like other objects, QThread objects live in the thread where the object was created – not in the thread that is created when QThread::run() is called. It is generally unsafe to provide slots in your QThread subclass, unless you protect the member variables with a mutex.
上面说的很明白了,QThread objects是存在于创建它的线程上,它的所有执行都在该线程上,但当调用run时,QThread objects会创建一个新的线程去执行它。(子类化的QThread的所有资源方法都是在主线程上,除非你是在run方法中创建的。)
而worker-object方法中,因为整个worker对象都通过movetoThread转移到线程上了,所以,在线程启动后,worker对象的所有槽函数的发射执行(调用执行不是)都是在分线程中执行的。
记住,当使用线程继承方法时,必须要明白,该继承的对象的run方法是在新线程上执行的同时,该对象的槽函数却仍是在主线程上执行。如果其成员变量在两类函数上都有使用,必须检查该变量的安全性(就是变量的在不同线程间的同步问题了)。
故推存使用worker-object
谁创建,谁执行。(object在哪个线程上创建,则其函数在哪个线程上执行)
每个线程各自维护一个独立的event_loop ;
创建后的对象可通过movetoThread转移到其它线程,断绝与当前线程的关系。
QThread对象的run函数是在一个单独的线程上执行的,而不是创建QThread对象的线程上执行。
slot 和 signal的连接方式:
参照worker-object的代码创建线程时,一个还好,多个线程就受不了了,每次都要在主线程里写一堆的start,quit,wait,如果线程里有个循环条件,还需要在主线程里将此条件置假才能正常退出线程,这样就相当麻烦,还不如用subclass方式, 为此自己使用如下变种,测试了是可行的。
class Worker : public QObject //对象的创建和销毁还是在主线程执行。
{
Q_OBJECT
bool doLoop;
public:
Worker(){
moveToThread(&workerThread);
workerThread.start();
}
~Worker(){
doLoop = false;
workerThread.quit();
workerThread.wait();//为保安全退出一定要加上。
}
QThread workerThread;
public slots:
void doWork(const QString ¶meter) {
QString result;
doLoop = true;
while(doLoop&&workerThread.isRunning())
{
/* ... 想在在线程中实现的代码 ... */
}
emit resultReady(result);
}
void doWork_other(const QString ¶meter);
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
public:
Controller() {
Worker *worker = new Worker;
Worker *worker_ohter = new Worker;
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
connect(this, &Controller::operate_other, worker_ohter, &Worker::doWork_other);
connect(worker_ohter, &Worker::resultReady, this, &Controller::handleResults);
}
~Controller()
{
delete worker;
delete worker_other;
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
void operate_other(const QString &);
};
这样子是不是省事很多?主线程只管创建对象,connect信号与槽就好,至于线程的结束那些就交管线程对象本身去管理好了,这样不容易弄混结束条件,而且由于线程thread是Worker对象的一个成员,因此可以直接用thread.isRuning 等这些线程执行情况去判断,同时,成员线程退出时不能去释放创建它的对象,所以不要再关联QThread::finished和QObject::deleteLater了。