QThread只有run函数是在新线程里的,其他所有函数都在QThread生成的线程里
如果QThread是在ui所在的线程里生成,那么QThread的其他非run函数都是和ui线程一样的,所以,QThread的继承类的其他函数尽量别要有太耗时的操作,要确保所有耗时的操作都在run函数里。
在UI线程下调用QThread的非run函数(其实也不应该直接调用run函数,而应该使用start函数),和执行普通函数无区别,这时,如果这个函数要对QThread的某个变量进行变更,而这个变量在run函数里也会被用到,这时就需要注意加锁的问题,因为可能这个变量前几毫秒刚刚在run中调用,再调用时已经被另外的线程修改了。
安全结束线程:
https://blog.csdn.net/czyt1988/article/details/64441443
1.工作线程
每个程序启动后拥有的第一个线程称为主线程,即GUI线程。QT中所有的组件类和几个相关的类只能工作在GUI线程(是唯一允许执行GUI相关操作的线程),不能工作在次线程,次线程即工作线程,主要负责处理GUI线程卸下的工作,每个线程都有自己的栈
2.多线程简介
QT线程类包括:
QThread 提供了跨平台的多线程解决方案
QThreadStorage 提供逐线程数据存储
QMutex 提供相互排斥的锁,或互斥量
QMutexLocker 是一个辅助类,自动对 QMutex 加锁与解锁
QReadWriterLock 提供了一个可以同时读操作的锁
QReadLocker与QWriteLocker 自动对QReadWriteLock 加锁与解锁
QSemaphore 提供了一个整型信号量,是互斥量的泛化
QWaitCondition 提供了一种方法,使得线程可以在被另外线程唤醒之前一直休眠
3.线程优先级
4.线程的执行
void run() :线程体函数,定义线程的功能
void start() :启动函数,讲线程入口地址设置为run函数
5.线程的退出
void quit()
退出线程,返回0表示成功,相当于QThread:exit(0)
void exit( int returnCode = 0 ) : thread退出event loop,并从exec返回,exec的返回值就是 returnCode。通常returnCode=0表示成功,其他值表示失败
void terminate() : 强制结束线程,不保证数据完整和资源释放
setTerminationEnabled ( bool enabled = true ) : 打开terminate()
6.线程的等待
bool wait ( unsigned long time = ULONG_MAX ) : 线程将会被阻塞,等待time毫秒,如果线程退出,则wait会返回。Wait函数解决多线程在执行时序上的依赖
void sleep ( unsigned long secs )
void msleep ( unsigned long msecs )
void usleep ( unsigned long usecs )
7.线程的状态
isRunning() : 线程是否运行
isfinished() : 线程是否结束
8.线程与事件循环
QThread中run()的默认实现调用了exec(),从而创建一个QEventLoop对象,由QEventLoop对象处理线程中事件队列(每一个线程都有一个属于自己的事件队列)中的事件。exec()在其内部不断做着循环遍历事件队列的工作,调用QThread的quit()或exit()方法使退出线程,尽量不要使用terminate()退出线程,terminate()退出线程过于粗暴,造成资源不能释放,甚至互斥锁还处于加锁状态。
线程的同步:
1.同步基础
临界资源:每次只允许一个线程进行访问的资源
线程间互斥:多个线程在同一时刻都需要访问临界资源
线程锁能够保证临界资源的安全性,通常,每个临界资源需要一个线程锁进行保护。
线程死锁:线程间相互等待临界资源而造成彼此无法继续执行。
产生死锁的条件:
A、系统中存在多个临界资源且临界资源不可抢占
B、线程需要多个临界资源才能继续执行
死锁的避免:
A、对使用的每个临界资源都分配一个唯一的序号
B、对每个临界资源对应的线程锁分配相应的序号
C、系统中的每个线程按照严格递增的次序请求临界资源
QMutex, QReadWriteLock, QSemaphore, QWaitCondition 提供了线程同步的手段。使用线程的主要想法是希望它们可以尽可能并发执行,而一些关键点上线程之间需要停止或等待。例如,假如两个线程试图同时访问同一个全局变量,结果可能不如所愿。
2.互斥量QMutex
QMutex 提供相互排斥的锁,或互斥量。在一个时刻至多一个线程拥有mutex,假如一个线程试图访问已经被锁定的mutex,那么线程将休眠,直到拥有mutex的线程对此mutex解锁。QMutex常用来保护共享数据访问。QMutex类所以成员函数是线程安全的。
头文件声明: #include <QMutex>
互斥量声明: QMutex m_Mutex;
互斥量加锁: m_Mutex.lock();
互斥量解锁: m_Mutex.unlock();
如果对没有加锁的互斥量进行解锁,结果是未定义的。互斥量的加锁和解锁必须在同一线程中成对出现。
QMutex ( RecursionMode mode = NonRecursive )
QMutex有两种模式:Recursive, NonRecursive
A、Recursive
一个线程可以对mutex多次lock,直到相应次数的unlock调用后,mutex才真正被解锁。
B、NonRecursive
默认模式,mutex只能被lock一次。
如果使用了Mutex.lock()而没有对应的使用Mutex.unlcok()的话就会造成死锁,其他的线程将永远也得不到接触Mutex锁住的共享资源的机会
bool tryLock();
如果当前其他线程已对该mutex加锁,则该调用会立即返回,而不被阻塞。
bool tryLock(int timeout);
如果当前其他线程已对该mutex加锁,则该调用会等待一段时间,直到超时
子类化QThread方式启动线程Demo
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QMutex>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
void startThread(); //开启线程
void stop(); //停止线程
protected:
void run(); //线程主执行函数
private:
volatile bool stopped; //线程停止位
QMutex m_mutex; //互斥锁
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QDebug>
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
stopped = false;
}
void MyThread::run()
{
qreal i = 0;
while (!stopped)
{
qDebug() << QString("in MyThread: %1").arg(i);
msleep(1000);
i++;
}
}
void MyThread::startThread()
{
m_mutex.lock();
stopped = false;
m_mutex.unlock();
start();
}
void MyThread::stop()
{
m_mutex.lock();
stopped = true;
m_mutex.unlock();
}