目录
1.概述
2.多线程(threading模块)
3.队列(queue模块)
4.多进程(multiprocessing模块)
之前学习的程序都是单步骤的,即执行完上一句才开始执行下一句,也称为单任务,单任务程序虽然简单易懂但性能低。多任务是解决这一问题引出的,它能同时让计算机执行多个任务,有着异步与高效的特点。Python中提供了threading、multiprocessing模块来实现多任务,接下来让我们学习一下吧。
1.概述
并发与非并发:
非并发编程:程序由单个步骤序列构成;包含独立子任务的程序执行性能低
并发编程:异步,高效;分解子任务、简化流程与逻辑
进程概念process:
是程序的执行实例
每个进程都有自己的地址空间、内存、数据栈与辅助数据
线程概念thread:
在同一进程内,可被并行激活的控制流
共享相同的上下文(即地址空间、数据结构)
便于信息共享和通信
线程的访问顺序差异会导致程序结果不一致
Python GIL全局解释器锁
Python代码由虚拟机(解释器主循环)控制,主循环同时只能有一个控制线程执行
这样导致Python的多线程只在IO密集型程序上有所作用,在计算密集型上只能用进程(但会消耗更多的资源)或者用底层语言编写
2.多线程(threading模块)
顺序执行一个4秒代码和2秒代码会消耗6秒时间,如下图
threading模块实现多线程
构造方法1:
.Thread(target = 目标函数,args=(参数,))
其他方法:
.start() 启动线程
.join() 要求主线程等待
.current_thread().name 获取当前线程名称
上述代码我们发现主线程不会等待子线程运行结束,我们希望主线程等待子线程需要使用.join()方法
构造方法2:
自定义Thread派生类,重新run方法逻辑(注意run方法内部将参数元组解包,详见Python基础知识学习(五)——函数)
当使用多线程时经常会发生多人同时使用资源而发生错乱的问题,如下图我们想让每个人按顺序放入123鸡蛋,错乱代码如下
想要解决这一问题最简单的方式是使用同步锁
创建锁:
threading.Lock()
锁方法:
.acquire() 获得锁
.release() 释放锁
锁也支持上下文操作:
with 锁:
使用上下文with的锁
3.队列(queue模块)
队列保证多线程环境下共享操作的顺序
队列分类:
先进先出队列 Queue
先进后出(栈)LifoQueue
优先队列 PriorityQueue
构造队列
queue.Queue(maxsize=0)
方法
.put(item, block=True, timeout=None) 放入数据项,timeout指定几秒超时
.get(block = True, timeout =None) 获取数据项
.Empty() 查看队列是否为空
.task_done() 声明当前队列任务处理完毕
.join() 队列所有项处理完毕前阻塞
执行结果:
4.多进程(multiprocessing模块)
由于Python的全局解释器锁使得多线程优化的结果非常有限,多线程仅仅适用于IO密集型的操作,对计算密集型没有太大效果。而多进程充分利用多核、多cpu的计算能力,适用于计算密集型任务。即IO密集型多考虑多线程,计算密集型多考虑多进程。
构造进程:
multiprocessing.Process(target=目标函数, args=(参数,))
方法:
.start() 启动线程
.join() 要求主进程等待
.current_process() 获取当前进程 进程名称 .name
进程池:
concurrent.futures 模块(弃用)——ThreadPoolExecutor 线程池、ProcessPoolExecutor 进程池
multiprocessing模块:现在进程池已经被添加到进程模块中
创建进程池 multiprocessing.Pool()
创建线程池 multiprocessing.ThreadPool()
阻塞方式运行 .apply(func, args)
非阻塞方式运行 apply_async(func, args)
关闭进程池 .close()
回收进程池 .join()