QT -- 新建线程的方法(四种办法,很详细,有截图)
看了不少Qt线程的东西,下面总结一下Qt新建一个线程的方法。
一、继承QThread
继承QThread,这应该是最常用的方法了。我们可以通过重写虚函数void QThread::run ()实现我们自己想做的操作,实现新建线程的目的。前面已经介绍了Qthread,这里就不重复了。
这种方法,我们每一次要新建一个线程都需要继承Qthread,实现一个新的类,有点不太方便。但是相对于Qrunnable,这种方法的好处就是我们可以直接调用对象的start()函数启动线程,而Qrunnable必须借助QthreadPool。
二、继承QRunnable
Qrunnable是所有可执行对象的基类。我们可以继承Qrunnable,并重写虚函数void QRunnable::run () 。我们可以用QThreadPool让我们的一个QRunnable对象在另外的线程中运行,如果autoDelete()返回true(默认),那么QThreadPool将会在run()运行结束后自动删除Qrunnable对象。可以调用void QRunnable::setAutoDelete ( bool autoDelete )更改auto-deletion标记。需要注意的是,必须在调用QThreadPool::start()之前设置,在调用QThreadPool::start()之后设置的结果是未定义的。
下面是我写的简单的例子:
class Runnable:publicQRunnable { //Q_OBJECT 注意了,Qrunnable不是QObject的子类。 public: Runnable(); ~Runnable(); void run(); protected: private: }; Runnable::Runnable():QRunnable() { } Runnable::~Runnable() { cout<<"~Runnable()"<<endl; } void Runnable::run() { cout<<"Runnable::run()thread :"<<QThread::currentThreadId()<<endl; cout<<"dosomething ...."<<endl; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); cout<<"mainthread :"<<QThread::currentThreadId()<<endl; Runnable runObj; QThreadPool::globalInstance()->start(&runObj); return.exec(); }
由结果可看出,run()确实是在不同于主线程的另外线程中运行的,而且在运行结束后就调用了析构函数,因为默认是可以自动被销毁的。
我们可以对同一个对象多次调用QThreadPool::start(),如果是可以自动被销毁的,Qrunnable对象会在最后一个线程离开了run函数之后才被销毁的。
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); cout<<"mainthread :"<<QThread::currentThreadId()<<endl; Runnable runObj; QThreadPool::globalInstance()->start(&runObj); QThreadPool::globalInstance()->start(&runObj); QThreadPool::globalInstance()->start(&runObj); returna.exec(); }
我三次调用QThreadPool::globalInstance()->start(&runObj);,但是在三次都执行完之后才运行析构函数。
这种新建线程的方法的最大的缺点就是:不能使用Qt的信号—槽机制,因为Qrunnable不是继承自QObject。所以我们要想知道线程是否运行结束或获取运行结果可能会比较麻烦。还有就是我们不能直接调用run()启动线程,必须借助于QthreadPool。
但是这种方法的好处就是,可以让QThreadPool来管理线程,QThreadPool会自动的清理我们新建的Qrunnable对象。
三、使用moveToThread
首先我们必须实现继承QObject的一个类,实现我们想要的功能。
class Worker:publicQObject { Q_OBJECT public: Worker(); ~Worker(); protected slots: voidfun1(); void fun2(); private: }; Worker::Worker():QObject() { } Worker::~Worker() { } void Worker::fun1() { cout<<"Worker::fun1() thread : "<<QThread::currentThreadId()<<endl; }
接着创建一个对象,并调用:moveToThread ( QThread * targetThread ),让对象在新的线程中运行。
int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); cout<<"mainthread :"<<QThread::currentThreadId()<<endl; QThread thread; Worker work; thread.start(); //注意记得启动线程 work.moveToThread(&thread); //由于不能直接调用worker //的函数,所以一般用信号触发调用 QTimer::singleShot(0,&work,SLOT(fun1())); QTimer::singleShot(0,&work,SLOT(fun2())); returna.exec(); }
这样就能让fun1()和fun2()都运行在thread线程中了。
需要注意的是:在work 的函数结束运行前,thread不能被析构。Thread的生命期不能小于work。否则的话程序就好崩掉了。
像下面的代码肯定是不行的。
void Dialog::startWork() { QThread thread; Worker*work = new Worker; thread.start(); work->moveToThread(&thread); QTimer::singleShot(0,work,SLOT(fun1())); QTimer::singleShot(0,work,SLOT(fun2())); }
所以thread 必须是new出来的。但是这样的话,就感觉有点麻烦,我们要同时管理thread和work,因为都是new 出来,我们需要负责清理。为了避免这样的麻烦,我想到的方法是,在work类中添加一个QThread成员。
class Worker:publicQObject { Q_OBJECT public: Worker(); ~Worker(); protectedslots: voidfun1(); voidfun2(); private: QThread m_thread; }; Worker::Worker():QObject() { m_thread.start(); this->moveToThread(&m_thread); }
这样我们在用的时候只需要newwork就行了。
四、使用QtConcurrent::run
其实前面也有用到QtConcurrent::run启动新线程了。QtConcurrent命名空间提供了很多方法可以实现并发编程,这个以后再深入探讨了,这里只是大概讲一下启动线程。还是用上面的worker代码作为例子:
void Worker::start() { QtConcurrent::run(this,&Worker::fun1); QtConcurrent::run(this,&Worker::fun2); } QtConcurrent::run是个模板函数,有很多种形式,我们也可以让全局的函数允许在另外的线程中。 void printMes(char*mes) { cout<<"pprintMes(char*mes) thread : "<<QThread::currentThreadId()<<endl; cout<<mes<<endl; } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); cout<<"mainthread :"<<QThread::currentThreadId()<<endl; char *mes= "hello world"; QtConcurrent::run(printMes,mes); returna.exec(); }
目前所知的新建线程的方法就大概这些了,希望对大家有用,可能还要别的,以后再继续学习了