前一段时间,趁着工作之余,重写了公司的一个多线程下载模块,顺便又重温了下qt的多线程相关部分。所以有了这个系列的文章。
众所周知,qt 创建多线程有主流的有3种方法:
1. 采用经典的QThread类。 2.实现QRunable 接口。3.采用qt并行框架Qtconcurrent。
既然有三种方法,那么哪种方式更好,在实际的项目中改采取哪种方式来实现?那么我们不得不对3种方式做下对比。下面我将对每
一种方式,表达下我的浅见。
QThread 类
这是一个经典得不能再经典的类,在很多语言中你可以看到他的身影。最最简单的用法就是重载QThread类,实现其中的run方法,
通过执行start(),来启动线程,于是这样的代码边随处可见:
class MyThread : public QThread { public: void run(); }; void MyThread::run() { //doSomething...... exec(); }
下面我们深入一点,将这个例子扩展下。 很多时候你可能要在创建QThread 类的时候,你需要给QThread类传递点东西进去,或者你想
在QThread 操作一下主线程里面的数据, 于是你可能会有了下面的代码:
class MyThread : public QThread { Q_OBJECT public: MyThread(QObject* pare, QObject& obj); ~MyThread(); void run(); private: QObject* m_pare; MyObject m_obj; }; MyThread::MyThread(QObject* pare) { m_pare = pare; } void MyThread::run() { //doSomething..... m_pare->doSomething();//error! m_obj.doGet();//error! MyObject testObject; testObject.doGet();//ok exec(); } MyObject obj; MyThread* mthd = new MyThread(this); mthd->start();
你一运行,就会有问题。你可能会看见qt告诉你不能操作m_obj 和通过m_pare 指针去操作。
为什么会这样,就得仔仔细细的分析下对象了, 先看看够着函数传入的obj对象,这个对象毫无疑问的是在主线程中创建的吧,那么显然他属于主线程,
当 mthd->start() 后run函数会在另外一个新开的线程中执行。mthd 是在主线程中创建的吧, 那么mthd 就是主线程的对象,存在于主线程中,所以
m_obj,m_pare都在主线程中,他们都是主线程的对象。 而run函数,是新线中,所以在新线程中操作的是主线程中的对象,调用的也是主线程的方法。
这就跨线程操作了。(当然qt也提供跨线程调用的方法QMetaMethod::invoke())。
上述情况可以通过moveToThread将对象移动到同一个线程中(推荐) QThread myThread; MyObject obj; obj.moveToThread(&myThread);//这样obj便在线程中了
具体的我一般喜欢这样写:
QThread* thread = new QThread(this); HttpDownloadWorker* worker = new HttpDownloadWorker(this, taskUrl(), fileName); worker->moveToThread(thread); worker->connect(thread, SIGNAL(started()), SLOT(doWork())); thread->connect(worker, SIGNAL(finished()), SLOT(quit())); thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));将所有的任务放到另外一个worker类中去实现,然后把worker移动到thread 线程中去,这样 thread 和worker 处于同一线程
doWork()函数便在thread 线程里面执行了,然后worker 的成员变量都属于thread类,就不会出现跨线程的问题了!
用qt的,应该没有人不用信号和槽吧,很多时候我们也需要跨线程进行信号槽操作,那么我们会遇到一个经典的问题,槽函数究竟会在哪个线程执行。
(注意:QObject的connect函数的第五个参数代表信号与槽的连接模式,如果发送信号的对象和接受信号的对象不再同一个线程,第五个参数应该为
Qt::QueuedConnection队列方式将信号转换成事件发送到槽函数所在线程的消息队列中让槽函数所在线程来处理。如果是在同一线程就直接用
Qt::DirectConnection连接方式)
连接方式区别