qt源码多线程编译-Qt多线程知识点整理

本书摘要来自:OpenCV3和Qt5计算机视觉应用开发

进程和线程的区别

(1)进程类似于单个程序,可以直接被操作系统执行

(2)线程是进程的子集,一个进程可以包含多个线程

(3)正常情况下,不同进程之间是没有任何关系的,不同线程共享显存和资源(注意,相互之间的交互可以通过操作系统提供的手段来实现)

应用实例:

(1)进度条、剩余工作比例、完成搜索所需时间等。

(2)计算机视觉中的视频或摄像头处理需要及时、正确地读取和处理显示的视频。

最好的方法是将实际任务和 GUI 分解为不同的任务。

1.Qt中的多线程

一般来说,Qt 有两种不同类型的多线程。

该方法基于QThread的底层方法,提供了很强的灵活性,有效实现了多线程的控制,需要更多的代码和耐心才能让线程准确工作等等,基于QTConcurrent命名空间(或者Qt的Concurrency) Framework),在应用程序中创建和运行多个任务的中级方式。常用类描述

QThread

该类是 Qt 框架中所有线程的通用类。 您可以从 QTread 派生泛型来创建需要重新运行的新线程,或者创建 QThread 的新实例并通过调用 moveToThread 函数将任何 Qt 对象 (QObject) 连接到它。 在新线程中

Q线程池

用于管理线程并允许复用现有线程来实现新功能,从而降低线程创建的成本。 每个Qt应用程序都包含一个全局QThreadPool实例,可以使用QThreadPool::globalInstance()静态函数Example来访问该实例。 QThreadPool 类可以与 QRunnable 类的实例一起使用来控制、管理和回收 Qt 应用程序中的可运行对象。

Q运行

它提供了另一种创建线程的方法。 这是 Qt 中所有可运行对象的子类。 与 QThread 不同,Qrunnable 不是 QObject 的基类。 它可以用作需要执行的一段代码的套接字。 您需要编译一个从 QRunnablede 类派生的纯虚函数 run() 并重绘它才能使用 Qrunnable。 因此,QRunnable 的实例由 QThreadPool 类管理。

QMutex、QMutexLocker、QSemaphore、QWsitCondition、QReadLocker、QWriteLocke

该类主要用来处理线程之间的同步任务。 根据情况,这个类可以用来防止各种问题,比如线程覆盖彼此的估计结果,线程视图读取或写入一次只能处理一个线程的设备,以及发出一些类似的问题,经常需要解决创建多线程应用程序时自动处理。

Qt并发

是一个命名空间,可用于使用中级 API 创建多线程应用程序。 这使得编写多线程应用程序变得更加容易,而无需处理互斥锁、信号量和线程同步问题。

Qfuture、QfutureWatcher、QFututetelerator 和 QFutureSynchronizer

该类与QtConcurrent命名空间一起使用来处理多线程和异步操作结果。

2.用QThread实现低级多线程

Qt中所有对UI的操作都必须放在主线程上,否则操作UI会造成卡顿。

2.1 通用QThread(重绘运行函数)

继承public:QThread()并重绘run函数,调用时调用start函数启动线程。

start:可用于启动尚未启动的线程。 该函数通过调用已实现的run函数来开始执行,但是可以将以下值传递给start函数来控制线程优先级。

终止:该函数仅在极端情况下使用,不推荐使用。 它将强制终止线程。

setTerminationEnable:可用于允许或禁止终止功能

wait:此函数可用于阻塞线程(强制等待),直到线程完成或达到超时值(以微秒为单位)。

requestInterruption 和 isRequestInterrupted:可用于设置和获取中断请求状态。 通过适当地使用此函数,您可以确保线程在可以永久运行的进程中间安全地停止。

isRunning和isFinished:可用于请求线程的执行状态。

据悉,QTread还有一些其他处理多线程的函数,如quit、exit、ideaThreadCount等。

2.2moveToThread函数

使用说明:首先创建一个继承自QObject的类,然后在类中使用new。 使用关键代码如下:

VideoProcess *process = new VideoProcess();
process->moveToThread(new QThread(this));
process->thread()->start()    //启动函数

注意:使用moveToThread时,有父对象的对象无法连接到新线程,且该对象是一个watch指针。

结束代码:

process->stopProcess()    //自定义停止函数
process->thread()->quit()   //终止函数
process->thread()->wait()    //直至线程结束或达到超时毫秒

注意:调用 quit() 时,其对象不应包含任何正在运行的循环或挂起的命令,否则会出现大问题。

3.线程同步工具

线程并行会造成资源竞争,线程同步工具会解决这个冲突。

3.1 互斥锁

互斥锁只是保护和防止多个线程同时访问统一对象实例的一种方法。 Qt 提供了一个名为 QMutex 的类来处理访问序列化问题。

QMetex me;
me.lock();
//代码段
me.unlock();

这些模式只能保证一个线程正在访问该对象,并且每次都需要对其进行锁定和解锁。 所以你可以使用QMutexLocker

QMetex me;
forver{
QMutexLocker locker(&me);
}

3.2 读写锁

虽然互斥锁功能强大,而且缺乏单独的功能,比如不同类型的锁,虽然串行化访问非常有用,但无法有效应用于读写串行化等问题。 此类问题主要依赖于两种不同类型的锁:读、写。 作为一个反例:您希望不同的线程能够同时读取一个对象,并且您希望保证在任何给定时间只有一个线程可以更改或写入该对象。 对于这些情况,您可以使用读写锁,它基本上是一种改进的互斥锁。 Qt框架提供了QreadWriteLock类,可以像QMetux类一样使用。 不同的是,它提供了一个用于读的锁函数(lockForRead)和另一个用于写的锁函数(lockForWrite)。

forever{
lock.lockForRead();
rad()
lock.unlock();
}

forever{
lock.lockForWrite();
rad()
lock.unlock();

或者也可以类似于QMutexLocker

forever{
QReadLocker locker(&lock);
rad()

forever{
QWriteLocker locker(&lock);
rad()

3.3 信号量

有时在多线程编程中,需要保证多个线程能够相应地访问有限数量的相同资源。 例如,运行程序的设备可能显存特别有限,因此我们更希望需要大量显存的线程考虑到这一事实,并根据可用显存量来执行相关操作。 多线程编程中类似的问题一般都会用到信号量。 处理。 信号量类似于增强型互斥体,不同之处在于它可以执行锁定和解锁操作,并且可以跟踪可用资源的数量。

Qt框架提供了一个Qsemaphore类,可以在多线程编程中使用信号量。 因为信号量是用来根据可用资源的数量来同步线程的,所以函数名(意思是信号量)也更适合这个目的,并且与lock函数和unlock函数不同,Qsemaphore类中可用的函数如下:

QSemaphore memSem(100);

但在每个线程中,在内存密集型进程之前和之后,会分别获取和释放所需的内存空间,如下所示:

memSem.acquire(x);
process_image();   memory intensive process
memSem.release(x);

注意:在上面的例子中,如果 . 这意味着可以通过调用释放函数来减少(或创建)可用资源的数量,并且释放函数释放的资源应该小于获得的资源数量。

3.4 等待条件

多线程编程中可能出现的另一个常见问题是假设除了操作系统正在执行的线程之外,线程还必须等待各个条件。 出现此问题。 在这种情况下qt源码多线程编译,线程自然会使用互斥锁或读写锁来阻塞其他线程。 由于这只是线程的轮换,但是线程在等待特定的条件,所以人们会感觉需要等待条件的线程被释放了。 然后互斥锁或读写锁进入休眠状态,以便其他线程可以继续运行。 当条件满足时,等待条件的线程将被另一个线程唤醒。

Qt框架有一个名为QWaitCondition的类,可以专门用来处理上述问题。 任何可能需要等待个别条件的线程都可以使用此类,例如:

假设有很多线程处理一个Mat类(图像),并且有一个线程负责读取图像(仅当图像存在时)。 假设某个进程、程序或用户负责创建该映像文件,因此该映像文件可能暂时不可用。 由于该图像被多个线程使用,因此需要互斥体来确保一次只有一个线程定位。 图像,如果图像仍然不存在,读取线程可能需要继续等待,所以:

forever
{
mutex.lock();
imageExistcond.wait(&mutex);
read_image();
mutex.unlock();
}

注意:在前面的示例中,互斥体的类型为 QMetux,imageExistsCond 的类型为 QWaitCondition。 前面的代码解释了互斥体被锁定才能开始读取图像,如果必须等待图像存在,则释放互斥体以方便其他线程可以继续工作,这就需要另一个线程来负责读取线。 因此:

forever{
if(QFile::exists("image.jpg"))
    imageExistsCond.wakeAll();
}

该线程仍然检查图像文件是否仍然存在。 如果存在,它将尝试唤醒所有等待此等待条件的线程。 您还可以使用wakeOne函数代替wakeAll。 wakeOne 只是尝试从具有等待条件的多个线程中随机唤醒一个线程。 如果您希望在满足条件时只让一个线程开始工作,则可以使用这些形式。

4.基于QtConcurrent的中间多线程

主要目的是使用不需要线程同步的工具创建多线程程序。 它的实现方式是使用任何平台的最优线程来处理数据列表。 以下函数一般使用中间API来处理多线程。

过滤器组合列表

过滤的

其工作方式与过滤器函数相同,不同之处在于过滤器返回混合列表而不是更新输入列表。

过滤减少

过滤函数的工作原理类似,但它还应用第二个函数将每个项目传递到混合器

地图

可用于将特定函数应用于列表中的所有项目(使用最优或定义自定义线程数)此外,map函数还需要一个列表和一个函数

映射的

与 map 的工作方式相同,不同之处在于,mapped 返回结果列表而不是更新输入列表

映射简化

使用map方法qt源码多线程编译,他还将第二个函数应用于第一个映射函数之后的每个项目

跑步

为了在单独的线程中轻松执行此函数

函数的返回值实际上指的是异步估计的结果。 Qtconcurrent在不同的线程中启动所有的估计,并会立即返回给调用者,并且只有在估计完成后才能得到结果。 这可以通过使用 Future 变量来完成,或者通过在 Qt 框架中实现 QFuture 和子类来完成。

QFuture类可用于检索QtConcurrent命名空间中的函数启动的估计结果,通过暂停、恢复等控制估计过程; 监控估算的进度。 为了使用Qt信号和槽来实现对QFuture类更灵活的控制,可以使用一个名为QFutureWatcher的类,该类包含一些信号和槽,可以通过进度条等控件来监视估计。


收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 源码编译 qt源码多线程编译-Qt多线程知识点整理 https://www.wkzy.net/game/200641.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务