1.初识线程
我们都知道Qt的GUI线程(主线程),在主线程内进行复杂,高速的刷新等操作会使得界面假死(即白屏,未响应状态),这时候就需要创建子线程,将复杂操作交给子线程来处理。
注意事项:Qt的线程不允许跨线程调用主线程UI,即所有的界面控件刷新和修改只能在主线程进行,子线程只负责处理数据,每个线程都有一个自己的事件循环!!!
2.QThread的函数
//线程构造
QThread::QThread(QObject *parent = nullptr)
//开启线程
thread->start();
//挂起线程
void msleep(unsigned long msecs)
void sleep(unsigned long secs)
void usleep(unsigned long usecs)
//线程强制退出
[slot] void QThread::terminate() //线程安全。
//线程释放(一般情况)
//当线程结束时会发送一个finished信号,我们将finished信号和deletelater作为信号槽就可以释放线程了
connect(thread,SIGNAL(finished()),thread,SLOT(deleteLater()));
thread->quit();
thread->wait();
//判断是否运行结束
bool isFinished() const
//判断是否正在运行
bool isRunning() const
3.QThread使用的几种方式
1.继承QThread,重写run函数。
优点:可以通过信号槽与外界进行通信。
缺点:每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。
要自己进行资源管理,线程释放和删除。并且频繁的创建和释放会带来比较大的内存开销。
适用场景:QThread适用于那些常驻内存的任务。
注意:这种方式只有run函数里执行的才是在线程中的。
2.moveToThread函数
好处:短小精悍,不需要你去创建线程。并且如果继承线程重写后可以复用。
mainwindow.cpp
myObject * ob = new myObject();
QThread * thread = new QThread;
ob->movetothread(thread);
//通讯用信号槽方式,这里就不写了,这里需要关注信号槽第五个参数,具体可以看信号槽详解那一章
thread->start();//开启线程
myObject.cpp
3.QRunnalble的run
Qrunnable是所有可执行对象的基类。我们可以继承Qrunnable,并重写虚函数void QRunnable::run () 。我们可以用QThreadPool让我们的一个QRunnable对象在另外的线程中运行,如果autoDelete()返回true(默认),那么QThreadPool将会在run()运行结束后自动删除Qrunnable对象。可以调用void QRunnable::setAutoDelete ( bool autoDelete )更改auto-deletion标记。需要注意的是,必须在调用QThreadPool::start()之前设置,在调用QThreadPool::start()之后设置的结果是未定义的。
一、实现方法:
1、继承QRunnable。和QThread使用一样, 首先需要将你的线程类继承于QRunnable。
2、重写run函数。还是和QThread一样,需要重写run函数,run是一个纯虚函数,必须重写。
3、使用QThreadPool启动线程
class Runnable:public QRunnable
{
//Q_OBJECT 注意了,Qrunnable不是QObject的子类。
public:
Runnable();
~Runnable();
void run();
};
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);
returna.exec();
}
4.QtConcurrent的run
Concurrent是并发的意思,QtConcurrent是一个命名空间,提供了一些高级的 API,使得在编写多线程的时候,无需使用低级线程原语,如读写锁,等待条件或信号。使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。这意味着今后编写的应用程序将在未来部署在多核系统上时继续扩展。
QtConcurrent::run能够方便快捷的将任务丢到子线程中去执行,无需继承任何类,也不需要重写函数,使用非常简单
1、首先在.pro文件中加上以下内容:QT += concurrent
2、包含头文件#include ,然后就可以使用QtConcurrent了
QFuture<T> QtConcurrent::run(Function function, ...)
QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)
extern void func(QString str)
{
qDebug() << __FUNCTION__ << str << QThread::currentThreadId()
<<QThread::currentThread();
}
void Widget::onBtnClicked()
{
QFuture<void> f1 =QtConcurrent::run(func,QString("this"));
f1.waitForFinished();
}
4.锁
1.QMutex的使用
QMutex locker ;
locker->lock();//在函数入口锁住
locker->unlock()l//函数结束前解锁
//lock和unlock必须绑定一起,即使用lock必须使用unlock
2.QMutexLocker的使用
QMutexLocker locker(m_mutex);
3.QReadWriteLock 的使用
Qt的 QReadWriteLock 默认优先级是写优先,即写锁的优先级>读锁。
QReadWriteLock readWriteLock;
QReadWriteLock 中常用的函数如下:
lockForRead() // 请求读锁
lockForWrite() // 请求写锁
tryLockForRead() // 尝试请求读锁,非阻塞函数,可以设置超时时间。
tryLockForWrite() // 尝试请求写锁,非阻塞函数,可以设置超时时间。
unlock() // 解锁(解读锁和解写锁,均使用该函数)