淄网站做网站,自动搜索关键词软件,wordpress配置虚拟主机,网站非法字符过滤【QT八股文】系列之篇章3 | QT的多线程 前言4. 多线程为什么需要使用线程池线程池的基础知识python中创建线程池的方法使用threading库队列Queue来实现线程池使用threadpool模块#xff0c;这是个python的第三方模块#xff0c;支持python2和python3 QThread的定义QT多线程知… 【QT八股文】系列之篇章3 | QT的多线程 前言4. 多线程为什么需要使用线程池线程池的基础知识python中创建线程池的方法使用threading库队列Queue来实现线程池使用threadpool模块这是个python的第三方模块支持python2和python3 QThread的定义QT多线程知识点怎么做多线程原理篇方案1信号与线程方案2线程方案3线程与队列 QT多线程的使用方法具体方法篇QT 多线程/QT线程同步的方法 5. QThread与QObjectQThread的定义对QObject的理解Q_OBJECT的作用是什么内部实现了些什么QObject是否是线程安全的/线程依附性是否可以改变/如何安全调用 下一章笔记说明 前言
第一篇章主要是基础定义及QT中重要的事件机制 笔记链接【QT八股文】系列之篇章1 | QT的基础知识及事件/机制第二篇章主要是QT的信号与槽以及通讯流程 笔记链接【QT八股文】系列之篇章2 | QT的信号与槽及通讯流程
这里我们主要件点更实际的也就是多线程以及QThread与QObject 因为介绍到信号与槽所以笔者我会讲通讯流程提前在前面来介绍
原创文章未经同意请勿转载
4. 多线程
为什么需要使用线程池
减少系统开销频繁创建/销毁线程的开销大影响处理效率。而在线程池缓存线程可用已有的闲置线程来执行新任务避免了创建/销毁带来的系统开销。避免阻塞问题线程并发数量过多抢占系统资源从而导致阻塞。线程能共享系统资源如果同时执行的线程过多就有可能导致系统资源不足而产生阻塞的情况。管理和控制线程运用线程池可以进行多线程的调度有利于延迟执行优先级执行和定时循环执行策略
线程池的基础知识
这里必须要知道线程池实现主要依靠两个部分一个是任务队列另外一个是线程的管理控制中心 很明显。任务队列的数据结构就是队列先进先出用Queue模块实现那先了解一下Queue:
Queue的常用方法 Queue.qsize()返回queue的大小。Queue.empty():判断队列是否为空通常不太靠谱。Queue.full():判断是否满了。Queue.put(item, blockTrue, timeoutNone): 往队列里放数据。Queue.put_nowait(item):往队列里存放元素不等待Queue.get(item, blockTrue, timeoutNone): 从队列里取数据。Queue.get_nowait(item):从队列里取元素不等待Queue.task_done()表示队列中某个元素是否的使用情况使用结束会发送信息。Queue.join()一直阻塞直到队列中的所有元素都执行完毕。
python中创建线程池的方法
这里必须要知道线程池实现主要依靠两个部分一个是任务队列另外一个是线程的管理控制中心
使用threading库队列Queue来实现线程池使用threadpool模块这是个python的第三方模块支持python2和python3使用concurrent.futures模块这个模块是python3中自带的模块python2.7以上版本也可以安装使用
使用threading库队列Queue来实现线程池
1、创建一个 Queue.Queue() 的实例然后使用数据对它进行填充。 2、将经过填充数据的实例传递给线程类后者是通过继承threading.Thread 的方式创建的。 3、生成守护线程池。 4、每次从队列中取出一个项目并使用该线程中的数据和 run 方法以执行相应的工作。 5、在完成这项工作之后使用 queue.task_done() 函数向任务已经完成的队列发送一个信号。 6、对队列执行 join 操作实际上意味着等到队列为空再退出主程序。
在使用这个模式时需要注意一点通过将守护线程设置为 true程序运行完自动退出。好处是在退出之前可以对队列执行 join 操作、或者等到队列为空。 代码 import Queue
import threading
import time
queue Queue.Queue()
class ThreadNum(threading.Thread):def __init__(self, queue):threading.Thread.__init__(self)self.queue queuedef run(self):while True:#消费者端从队列中获取numnum self.queue.get()print(Retrieved, num)time.sleep(1) #在完成这项工作之后使用 queue.task_done() 函数向任务已
经完成的队列发送一个信号self.queue.task_done()print(Consumer Finished)
def main():#产生一个 threads pool, 并把消息传递给thread函数进行处理这
里开启10个并发for i in range(5):t ThreadNum(queue)t.setDaemon(True)t.start()#往队列中填数据for num in range(10):queue.put(num)#wait on the queue until everything has been processedqueue.join()if __name__ __main__:main()time.sleep(500)
输出为
(Retrieved, 0)
(Retrieved, 1)
(Retrieved, 2)
(Retrieved, 3)
(Retrieved, 4)
(Retrieved, 5)
(Retrieved, 6)
(Retrieved, 7)
(Retrieved, 8)
(Retrieved, 9)注意运行main函数后继续执行time.sleep(500)可以观察到主线程未结束的情况下ThreadNum(queue)生成的线程还在运行。如果需要停止线程的话可以对以上代码加以修改 代码 import Queue
import threading
import time
queue Queue.Queue()
class ThreadNum(threading.Thread):没打印一个数字等待1秒并发打印10个数字需要多少秒def __init__(self, queue):threading.Thread.__init__(self)self.queue queuedef run(self):done Falsewhile not done:#消费者端从队列中获取numnum self.queue.get()if num is None:done Trueelse:print(Retrieved, num)time.sleep(1) #在完成这项工作之后使用 queue.task_done() 函数向任务已
经完成的队列发送一个信号self.queue.task_done()print(Consumer Finished)
def main():#产生一个 threads pool, 并把消息传递给thread函数进行处理这
里开启10个并发for i in range(5):t ThreadNum(queue)t.setDaemon(True)t.start()#往队列中填错数据for num in range(10):queue.put(num)queue.join()time.sleep(100)for i in range(10):queue.put(None)print(None)time.sleep(200)if __name__ __main__:start time.time()main()printElapsed Time: %s % (time.time() - start)main函数执行完后队列向线程发送None消息触发线程的停止标识这样就可以动态管理线程池了。
使用threadpool模块这是个python的第三方模块支持python2和python3 QThread的定义
QThread 是 QT 中用于创建线程的类它提供了一组方法用于启动、停止、监测线程的运行状态以及获取线程的相关信息。
QThread 类包含了多个方法用于启动、停止、监测线程的运行状态以及获取线程的相关信息。其中:
run()方法启动线程的执行stop()方法停止线程的执行join()方法等待线程的执行完毕detach() 方法将线程从事件循环中移除。
此外setName()、setId()、setPriority()、getPriority()、setSignalsBlocked()、isSignalsBlocked()等方法用于修改线程的属性。
QT多线程知识点
多线程运行机制当启动多线程后注册信号槽函数为主线程中的函数当任务完成后发射信号在主线程中对UI进行更新。【QThred】
怎么做多线程原理篇 方案1信号与线程
程序启动创建一个线程存活周期直到软件关闭当点击事件发生发送信号该信号连接两个槽A负责界面变化切换B进行后台通讯B通讯结束再通过信号将结果返回到界面切换通过这种机制实现界面与通信的分离。 结论 经过代码测试发现这个方案并非是异步的而是同步的原因是同时连接两个槽这个槽机制应该是一个列表串行执行的必然两个槽的执行会存在先后问题当一个阻塞另一个也就阻塞了。
方案2线程
程序启动当点击事件发生发送信号该信号连接一个槽槽负责界面变化切换同时创建一个线程存活周期报文发送接收完成既关闭线程进行后台通讯通讯结束再通过信号将结果返回到界面切换通过这种机制实现界面与通信的分离。线程中发送的数据要通过线程创建时传入。 结论 该方法虽然实现了界面切换与通讯的异步处理但是每点击一次按钮都需要一次线程的创建而且对于一直保持通讯的心跳机制还需要单独起一个线程可谓是花费巨大感觉不是很好的方法
方案3线程与队列
为了解决方案2中频繁创建线程的问题现在做如下改进程序启动创建一个线程存活周期直到软件关闭在线程中创建多个队列线程监控队列队列分别有信号队列信息发送队列当前界面位置队列当界面事件发生去修改队列线程则监控队列取出队列进行处理处理之后将结果返回 结论 这样的处理机制避免了线程的频繁创建同时能存储一些全局的重要信息也实现了异步的效果。
QT多线程的使用方法具体方法篇 方法一利用python的threading库实现主要使用threading.Thread类 线程启动使用start()函数如果需要等待线程执行使用join这样主线程会阻塞 使用join方法会让主线程阻塞在这里等待子线程结束在里面可以设置阻塞的时间 方法二继承QThread并重写run函数 使用QThread类来创建和管理多线程。具体步骤包括继承QThread类并重写其run()函数将需要在子线程中执行的代码放入run()函数中【保证线程安全】在主线程中创建QThread对象将其指针作为参数传递给需要在子线程中执行的对象调用QThread对象的start()函数来启动子线程。在线程任务执行过程中可以使用 QThread 的 join() 方法等待线程执行完毕。 ① 创建一个类从QThread类派生 ② 在子线程类中重写 run 函数, 将处理操作写入该函数中 ③ 在主线程中创建子线程对象, 启动子线程, 调用start()函数 需要注意的事项如果是while循环想要结束线程调用QThread::quit是没有用因为这样的线程根本就不需要事件循环比较好的方法就是把while内的控制变量设置为false或者直接使用Qt很不推荐的方法QThread::terminate。terminate()强制退出。 方法三使用线程池 QtConcurrent运行一个线程池它是一个更高级别的API不适合运行大量的阻塞操作如果你做了很多阻塞操作你很快就会耗尽池并让其他请求排队在那种情况下QThread(较低级别的构造)可能更适合于操作(每个代表一个线程)。 方法四利用QRunnable 类 QRunnable 类是 PyQt5 中的可运行对象类它提供了 run() 方法来执行线程并可以通过 QRunnableInterface 实现线程通信。QRunnable 类创建线程的基本原理是创建一个 QRunnable 实例并将其作为参数传递给 QApplication 的 thread() 方法创建线程。在线程任务执行过程中可以使用 QRunnable 的 run() 方法执行线程任务。QRunnable 对象可以访问主线程的 QCoreApplication 对象。在 QRunnable 对象中需要使用 start() 方法启动线程并使用 join() 方法等待线程执行完毕。 方法五继承QObject并将对象移动至子线程QThread ① 将业务处理抽象成一个业务类, 在该类中创建一个业务处理函数 ② 在主线程中创建一QThread类对象 ③ 在主线程中创建一个业务类对象 ④ 将业务类对象移动到子线程中 ⑤ 在主线程中启动子线程 ⑥ 通过信号槽的方式, 执行业务类中的业务处理函数 多线程使用注意事项: 业务对象, 构造的时候不能指定父对象 子线程中不能处理ui窗口(ui相关的类) 子线程中只能处理一些数据相关的操作, 不能涉及窗口
QT 多线程/QT线程同步的方法
使用 QMutex 对象:QMutex 是 QT 中用于线程同步的同步原语。每个线程都可以访问一个 QMutex 对象通过 lock() 和 unlock() 方法实现线程同步。当一个线程需要访问共享资源时它会首先尝试获取 QMutex 对象的锁如果锁已经被其他线程获取了那么该线程将被阻塞直到锁被释放。使用 QSemaphore 对象:QSemaphore 是 QT 中用于线程同步的同步原语。每个线程都可以访问一个 QSemaphore 对象通过 semaphore.wait() 和 semaphore.signal() 方法实现线程同步。当一个线程需要访问共享资源时它会首先尝试等待 QSemaphore 对象的许可如果许可已经被其他线程获取了那么该线程将被阻塞。使用 QWaitCondition 对象:QWaitCondition 是 QT 中用于线程同步的同步原语。它结合了 QMutex 和 QSemaphore 的特点可以更方便地实现线程同步。QWaitCondition 对象包含一个互斥锁和一个信号槽当一个线程需要等待条件满足时它会挂起并等待互斥锁的释放当条件满足时该线程会被唤醒并执行相应的操作。使用 QEventLoop 对象:QEventLoop 是 QT 中用于处理事件循环的类它可以实现线程同步。每个线程都可以创建一个 QEventLoop 对象当线程需要访问共享资源时它会进入 QEventLoop 对象的 eventLoop() 方法等待事件处理完毕再继续执行。 QReadWriteLock类 》一个线程试图对一个加了读锁的互斥量进行上读锁允许 》一个线程试图对一个加了读锁的互斥量进行上写锁阻塞 》一个线程试图对一个加了写锁的互斥量进行上读锁阻塞、 》一个线程试图对一个加了写锁的互斥量进行上写锁阻塞。 读写锁比较适用的情况是需要多次对共享的数据进行读操作的阅读线程。 QReadWriterLock 与QMutex相似除了它对 “read”,write访问进行区别对待。它使得多个读者可以共时访问数据。使用QReadWriteLock而不是QMutex可以使得多线程程序更具有并发性。信号量QSemaphore 但是还有些互斥量资源的数量并不止一个比如一个电脑安装了2个打印机我已经申请了一个但是我不能霸占这两个你来访问的时候如果发现还有空闲的仍然可以申请到的。于是这个互斥量可以分为两部分已使用和未使用。QReadLocker便利类和QWriteLocker便利类对QReadWriteLock进行加解锁
5. QThread与QObject
QThread的定义
QThread 是 QT 中用于创建线程的类它提供了一组方法用于启动、停止、监测线程的运行状态以及获取线程的相关信息。
QThread 类包含了多个方法用于启动、停止、监测线程的运行状态以及获取线程的相关信息。其中:
run()方法启动线程的执行stop()方法停止线程的执行join()方法等待线程的执行完毕detach() 方法将线程从事件循环中移除。
此外setName()、setId()、setPriority()、getPriority()、setSignalsBlocked()、isSignalsBlocked()等方法用于修改线程的属性。
对QObject的理解
Q_OBJECT 是 Qt 框架中的一个宏定义用于在类的声明中标记该类需要使用 Qt 的元对象系统Meta-Object System。使用 Q_OBJECT 宏定义后编译器会在编译期自动生成元对象代码包括信号signal和槽slot的注册、元对象信息的注册等等。QObject 类是Qt 所有类的基类。QObject是Qt对象模型的核心。这个模型的中心要素就是一种强大的叫做信号与槽无缝对象沟通机制。你可以用 connect() 函数来把一个信号连接到槽也可以用disconnect() 函数来破坏这个连接。为了避免永无止境的通知循环你可以用blockSignal() 函数来暂时阻塞信号。保护函数 connectNotify() 和 disconnectNotify() 可以用来跟踪连接。
对象树都是通过QObject 组织起来的当以一个对象作为父类创建一个新的对象时这个新对象会被自动加入到父类的 children() 队列中。这个父类有子类的所有权。能够在父类的析构函数中自动删除子类。可以通过findChild()和findChildren() 函数来寻找子类。
每个对象都一个对象名称objectName() 而且它的类名也可以通过metaObject()函数。你可以通过inherits() 函数来决定一个类是否继承其他的类。当一个对象被删除时它会发射destory() 信号你可以抓住这个信号避免某些事情。
对象可以通过event() 函数来接收事情以及过滤来自其他对象的事件。就好比installEventFiter() 函数和eventFilter() 函数。childEvent() 函数能够重载实现子对象的事件。
QObject还提供了基本的时间支持QTimer类 提高了更高层次的时间支持。
任何对象要实现信号与槽机制Q_OBJECT 宏都是强制的。你也需要在源原件上运行元对象编译器。不管是否真正用到信号与槽机制最好在所有QObject子类使用Q_OBJECT宏以避免出现一些不必要的错误。
所有的Qt widgets 都是基础QObject。如果一个对象是widget,那么isWidgetType()函数就能判断出。
Q_OBJECT的作用是什么内部实现了些什么
Q_OBJECT 是 Qt 框架中的一个宏定义用于在类的声明中标记该类需要使用 Qt 的元对象系统Meta-Object System。使用 Q_OBJECT 宏定义后编译器会在编译期自动生成元对象代码包括信号signal和槽slot的注册、元对象信息的注册等等。
具体来说使用 Q_OBJECT 宏定义后编译器会为该类生成一个 QMetaObject 对象该对象包含了该类的元对象信息包括类名、信号和槽的名称、参数类型等等。这些信息可以通过 QObject::metaObject() 函数获取到。
此外使用 Q_OBJECT 宏定义后还可以在该类中使用信号和槽使用 emit 关键字来发射信号使用 connect 函数将信号和槽连接起来。这些功能都是通过 Qt 的元对象系统实现的。
需要注意的是使用 Q_OBJECT 宏定义的类必须直接或间接继承自 QObject 类。
实现原理 Q_OBJECT 宏定义会为该类自动添加一些成员变量和成员函数用于支持 Qt 的元对象系统。 1、 QObject 类的虚函数 metaObject()它返回一个描述该对象的元对象。 2、QMetaObject 类型的静态变量用于存储该对象的元对象。
QObject是否是线程安全的/线程依附性是否可以改变/如何安全调用
QObject及其所有子类都不是线程安全的但都是可重入的。因此你不能有两个线程同时访问一个QObject对象除非这个对象的内部数据都已经很好地序列化例如为每个数据访问加锁。可以改变QObject的线程依附性。 调用QObject::moveToThread()函数。该函数会改变一个对象及其所有子对象的线程依附性。如何安全的在另外一个线程中调用QObject对象的接口 多线程机制设计将事件提交到接收对象所在线程的事件循环当事件发出时响应函数就会被调用。
下一章笔记
下篇笔记链接【QT的性能优化及异常处理】 下篇笔记主要内容【待更新】
说明
码字不易可能当中存在某些字体错误笔者我没有发现如果有错误欢迎大家指正。 另外因为笔记是之前做的这里我只把我之前做的搬移和重新排版过来如果有知识上的错误也欢迎大家指正。