Qt中的线程是与平台无关的
QThread 提供了创建一个新线程的方法
新建一个线程,继承QThread并重写它的run()当调用 start()函数时会调用重载的run()函数
在run()返回后线程就会退出,在线程中将不会有任何的事件循环运行除非调用exec()。
注意一个线程实例位于实例化它的旧线程中,而非调用run()的新线程中,这意味着所有线程的排队槽将在旧线程中执行。因此,开发人员希望在新线程调用槽必须使用worker-object方法,新槽不应直接在子类化QThread中来实现。当子类化QThread时,请记住,构造函数在旧线程中执行,然而run()在新线程中执行。如果一个成员变量的访问来自两个函数,然后从两个不同的线程访问变量,需要检查这样做是否安全。
注:用在不同的线程中的对象进行交互时必须小心。详见同步线程(Synchronizing Threads)。
1. 简单示例
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
bool stop ;
explicit MyThread(QObject *parent = 0);
void run();
signals:
public slots:
};
#endif // MYTHREAD_H
#include "mythread.h"
#include<QDebug>
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
stop = false;
}
void MyThread::run()
{
for(int i=0;i<1000;i++)
{
if(stop)break;
qDebug()<<i;
QThread::sleep(1);
}
}
#include <QCoreApplication>
#include "myobject.h"
#include <QThread>
#include<QDebug>
#include "mythread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread myThread;
myThread.start();
QThread::sleep(10);
myThread.stop=true;
return a.exec();
}
2. 线程互锁
看一下Qt中包含的线程类:
QThread 所有线程的基类,提供了创建一个线程的方法
QThreadStorge 提供一逐线程数据存储
QMutex 提供相互排斥的锁,或互斥量
QMutexLocker 可以自动对QMutex加锁与解锁
QReadWirterLock 提供了一个可以同时读操作的锁
QreadLocker与QwriteLocker可以自动对QReadWriteLock加锁与解锁
QSempHore提供了一个整形信号量,是互斥的泛化
QWaitCondition提供了一种方法,使线程可以在被另外线程唤醒之前一直休眠
线程的同步
QMutex 提供相互排斥的锁,或互斥量
QMetex提供了lock和Unlock函数,如果 一个已经锁定 这个互斥量,只有这个线程unlock后其它线程才可以访问
- 把上边的例子做一下修改,只是为了方便大家理解,这个例子没什么作用
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class QMutex;
class MyThread : public QThread
{
Q_OBJECT
private:
QMutex qm;
bool stop;
public:
explicit MyThread(QObject *parent = 0);
void run();
void SetFlg(bool flg);
signals:
public slots:
};
#endif // MYTHREAD_H
#include "mythread.h"
#include<QDebug>
#include<QMutex>
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
stop = false;
}
void MyThread::SetFlg(bool flg)
{
qm.lock();
stop=flg;
qm.unlock();
}
void MyThread::run()
{
for(int i=0;i<1000;i++)
{
qm.lock();
if(stop)
{
qm.unlock();
break;
}
qDebug()<<i;
QThread::sleep(1);
qm.unlock();
}
}
#include <QCoreApplication>
#include "myobject.h"
#include <QThread>
#include<QDebug>
#include "mythread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread myThread;
myThread.start();
QThread::sleep(10);
myThread.SetFlg(true);
return a.exec();
}
3. QMutexLocker
使用QMutex时要小心因为如果 lock()后没有unlock()会导致死锁别的线程就
永远也不能访问资源了
Qt提供了QMutexLocker来解决这个问题
修改我们的app文件
#include "mythread.h"
#include<QDebug>
#include<QMutex>
#include<QMutexLocker>
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
stop = false;
}
void MyThread::SetFlg(bool flg)
{
QMutexLocker locker(&qm);
stop=flg;
}
void MyThread::run()
{
QMutexLocker locker(&qm);
for(int i=0;i<1000;i++)
{
if(stop)
{
break;
}
qDebug()<<i;
QThread::sleep(1);
}
}
QMutexLocker会自己unluck:: QMutexLocker接收一个QMutex作为参数,当创建QMutexLocker对象时,就对关联的Mutex进行了Lock操作,直到该QMutexLocker对象被销毁,相关的QMutex才被Unlock。
QMutexLocker也提供了一个mutex()成员函数返回QMutexLocker操作的互斥量。对于需要访问互斥量是十分有用的,比如QWaitCondition::wait()。
QReadWirterLock
用mutext进行线程同步有一个问题某个时刻只许一个线程访问资源如果同时有多个线程对共享资源进行访问,同时有写操作线程那么这种情况下采用mutex就会成为程序的瓶颈。使用QReadWriteLock来实现多线程读操作,一个线程写操作,写线程执行时会阻塞所有的读线程,而读线程运行不需要进行同步
QReadWriteLock lock;
void ReaderThread::run()
{
...
lock.lockForRead();
read_file();
lock.unlock();
...
}
void WriterThread::run()
{
...
lock.lockForWrite();
write_file();
lock.unlock();
...
}
QreadLocker和QWriteLocker类是对QReadWirterLock 的简化
它们会自动unluck();
QReadWriteLock lock;
QByteArray readData()
{
QReadLocker locker(&lock);
...
return data;
}
QReadWriteLock lock;
QByteArray readData()
{
lock.lockForRead();
...
lock.unlock();
return data;
}
4. 线程管理
QThread会通知你触发了一个信号当线程started()和finished()时,或者使用isFinished()和isRunning()来查询线程的状态。
可以通过调用exit()或quit()来停止线程。在极端情况下,你可能要强行terminate()一个执行线程。但是,这样做是危险的。请阅读文档查看terminate()和setTerminationEnabled()的详细信息。
从Qt4.8起,可以释放运行刚刚结束的线程对象,通过连接finished()信号到QObject::deleteLater()。
使用wait()来阻塞调用的线程,直到其他线程执行完毕(或者直到指定的时间过去)。
QThread中还提供了静态的、平*立的休眠功能:sleep()、msleep()、usleep()允许秒,毫秒和微秒来区分,这些函数在Qt5.0中被设为public。
注意:一般情况下,wait()和sleep()函数应该不需要,因为Qt是一个事件驱动型框架。而不是wait(),关心监听信号finished()。取代sleep(),可以考虑使用QTimer。
静态函数currentThreadId()和currentThread()返回标识当前正在执行的线程。前者返回该线程的平台特定的ID,后者返回一个线程指针。
要设置线程的名称,可以在启动线程之前调用setObjectName()。如果不调用setObjectName(),线程的名称将是线程对象的运行时类型(上例中“WorkerThread”,因为这是QThread子类的类名)。请注意,基于Windows的构建版本目前不可用。
5. 参考文章
http://www.cnblogs.com/li-peng/p/3653781.html
http://blog.sina.com.cn/s/blog_a6fb6cc90101hohu.html
http://blog.csdn.net/calm_agan/article/details/6301365