Python进程(Process)、线程(Thread)、协程(asyncio)编程
一、多进程编程
-
进程概念
进程是计算机操作系统进行资源分配的基本单位,是一个程序的执行实例,也就是正在执行的程序。在OS的眼里,进程就是一个担当分配系统资源CPU时间、内存的实体。一般来说,系统会由一组进程组成:操作系统进程执行系统代码,而用户进程执行用户代码,计算机通过 CPU 的多路复用,所有这些进程可以并发执行,因此进程的可执行数量和CPU性能有关。
1、进程的基本状态
- 就绪:进程的就绪状态是指一个进程已经具备除处理器(CPU)之外的所有执行条件
- 运行:进程的就绪状态是指进程得到了处理器,并不需要等待其他任何资源,正在执行的状态
- 阻塞:进程阻塞状态也称进程等待状态,是指进程等待某一特定事件的出现(如I/O操作),在该过程中,进程依旧位于内存内,且占有CPU资源
2、图示
-
Process基本使用示例
-
Process简介
python内置的multiprocessing模块提供了类来方便用户创建一个多进程,其基本结构和参数如下:
-
结构
calss multiprocessing.Process(group=None, target=None, name=None, args=(),kwargs={}, *, daemon=None)
-
参数
- target: 调用对象,一般为函数、类,是进程的执行实体
- name: 进程的别名
- args:调用对象的位置参数元组
- kwargs:调用对象的字典
- group:基本不使用,忽略
-
Process类提供的方法
- is_alive(): 返回进程是否激活
- join(]): 阻塞进程,直到进程执行完成或者终止
- run(): 进程运行的函数,可以被重写
- start(): 激活进程,进程开始运行
- terminate(): 进程终止
-
-
创建一个Process进程
import os import time from multiprocessing import Process def task_process(delay): num = 0 for i in range(delay * 100000): num += i print(f"进程:{os.getpid()},执行完成") if __name__ == '__main__': print('父进程为%s.' % os.getpid()) t0 = time.time() # 单线程 task_process(3) task_process(4) t1 = time.time() print(f"单线程顺序执行耗时{t1 - t0}") # 多线程 p0 = Process(target=task_process, args=(3,)) p1 = Process(target=task_process, args=(4,)) t2 = time.time() p0.start() p1.start() p0.join() p1.join() t3 = time.time() print(f"多线程并发执行耗时{t3 - t2}")
-
创建一个自定义进程继承Process
import os import time from multiprocessing import Proces class self_process(Process): def __init__(self, delay): self.delay = delay super().__init__() def run(self): num = 0 for i in range(self.delay*10000): num += i print(f"进程:{os.getpid()},执行完成") if __name__ == '__main__': print('父进程为%s.' % os.getpid()) print("自定义线程") t0 = time.time() for i in range(5): q0 = self_process(i) q0.start() q0.join() t1 = time.time() print(f"自定义线程执行耗时{t1 - t0}")
-
-
线程概念
线程也称轻量级进程,线程是操作系统进行运算调度的最小单位,一般来说一个进程包含多个线程,至少包含一个线程,即线程是进程的实际运作单位,线程自身并不用于系统资源,仅仅需要必备的基本资源,但是线程之间可以共享属于同一个进程的全部资源,同一个进程中的线程可以并发执行,一个线程可以创建和销毁另一个线程,因此,多线程中,一个线程异常可能会导致整个多线程异常,但是多进程不会,因为各个进程的资源是独立的。
1、线程的基本状态
- 就绪:线程的就绪状态是指一个线程已经具备运行的所有条件,等待处理机,逻辑上已经可以运行
- 运行:线程的就绪状态是指线程占有处理机,并且正在执行的状态
- 阻塞:线程阻塞状态是指线程等待某一特定事件或者信号量 ,逻辑上不可运行
2、图示
-
Thread基本使用示例
-
Thread简介
python提供Thread类实现线程功能,其基本结构和参数和进程一致,如下:
-
结构
class Thread(group=None, target=None, name=None, args=(), kwargs={})
-
参数
- target: 调用对象,一般为函数、类,是进程的执行实体
- name: 进程的别名
- args:调用对象的位置参数元组
- kwargs:调用对象的字典
- group:基本不使用,忽略
-
Process类提供的方法
- is_alive(): 返回进程是否激活
- join(]): 阻塞进程,直到进程执行完成或者终止
- run(): 进程运行的函数,可以被重写
- start(): 激活进程,进程开始运行
- terminate(): 进程终止
-
-
创建一个Thread线程
import os import time import threading def task_process(delay): num = 0 for i in range(delay * 100000): num += i print(f"线程:{threading.current_thread().name},执行完成") if __name__ == '__main__': print(f"主线程为:{threading.main_thread().name}") t0 = time.time() # 多线程 p0 = threading.Thread(target=task_process, args=(3,)) p1 = threading.Thread(target=task_process, args=(4,)) t2 = time.time() p0.start() p1.start() p0.join() p1.join() t3 = time.time() print(f"多线程并发执行耗时{t3 - t2}")
-
创建一个自定义Thread线程继承Thread类
import os import time import threading class thread_process(threading.Thread): def __init__(self, delay): self.delay = delay super().__init__() def run(self): num = 0 for i in range(self.delay * 10000): num += i print(f"线程:{threading.current_thread().name},执行完成") if __name__ == '__main__': print(f"主线程为:{threading.main_thread().name}") print("自定义线程") t0 = time.time() for i in range(5): q0 = thread_process(i) q0.start() q0.join() t1 = time.time() print(f"自定义线程执行耗时{t1 - t0}")
-
三、协程编程
-
协程概念
协程是轻量级的线程,拥有自己的寄存器和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,切回来时,恢复之前保存的状态,因此协程能够保留上一次调用的状态信息,即每次过程重入,相当于进入上一次调用状态。协程适用于IO密集型任务处理,因为协程是基于线程的,其调用是在一个线程中进程,是单线程操作,因此效率略高于多线程。
-
asyncio基本使用示例
-
asyncio简介
python常用的协程库比如asyncio,结合python的async/await,能够很方便的使用协程操作。
-
基本使用
import asyncio import time async def task(): print(f"{time.strftime('%H:%M:%S')} task 开始") await asyncio.sleep(1) print(f"{time.strftime('%H:%M:%S')} task 结束") loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) tasks = [task() for _ in range(5)] start = time.time() loop.run_until_complete(asyncio.wait(tasks)) loop.close() end = time.time() print(f"用时{end - start}秒")
-
四、区别和联系以及应用场景
-
区别和联系
类型 区别和联系不同点 多进程 能同时处理多个任务,多进程目的是充分使用CPU性能,完成多个任务的处理,
适合用于大量计算的任务(计算密集型),于线程的联系是进程包含线程。
一个进程至少包含一个线程,线程是进程运行实体。多线程 能同时处理多个任务,线程是操作系统进行运算调度的最小单位,被包含于进程之中。
线程能够共享同一进程的资源,因此使用多线程能够极大的提高资源的利用率,让程序响应更快。协程 能同时处理多个任务,协程是轻量级线程,是单线程,
协程的调度处理都在一个线程内进行,即线程包含协程,
协程的效率略高于线程,同线程一样,适用于IO密集型任务处理。 -
应用场景重复计算分类及其选择
-
-
计算密集型 - 多进程/协程 - CPU利用率
计算密集型就是计算、逻辑判断量非常大而且集中的类型,因为主要占用CPU资源所以又叫CPU密集型,而且当计算任务数等于CPU核心数的时候,是CPU运行效率最高的时候,因此计算密集型任务特别消耗CPU。
-
IO密集型 - 多线程 - 网络(输入/输出)
IO密集型即在计算和处理时需要频繁大量的输入输出输出操作的类型,比如磁盘的读取数据和输出数据非常大的时候就是属于IO密集型,由于IO操作的运行时间远远大于CPU、内存运行时间,所以任务的大部分时间都是在等待IO操作完成,IO的特点是CPU消耗小,所以,IO任务越多,CPU效率越高,当然不是越多越好,有一个极限值。
-
-