本文共 4131 字,大约阅读时间需要 13 分钟。
我们之前写的代码,以函数为例, 都是先执行完一个函数之后,才能执行下一个函数,但实际情况是, 我们需要多个函数同时进行。同一时间段内同时执行多个任务(函数或方法),就是多任务
多任务有两种表现形式:并发和并行
现在我们的计算机CPU都是多核CPU,我们之前写的代码是从上往下单任务执行的,只能使用CPU中的一个核心,使用多任务可以充分利用CPU多个核心的计算能力,提高执行效率
并发:CPU核心数小于任务数,CPU轮流(交叉)执行多个任务
并行*CPU核心数大于等于任务数,每个CPU核心可以单独执行一个任务
从代码开始运行起来我们就称之为进程,所有代码执行完毕,进程结束
代码执行需要向操作系统申请内存资源,而进程是操作系统资源分配的最小单位,也就是说每启动一个进程,操作系统都会给其分配一定的运行资源(内存资源)保证进程的运行
程序运行起来, 在所有代码执行之前,会先默认创建一个主进程(即内存资源分配的过程),该进程会默认包含一个线程(即主线程),代码的执行实际上是由线程完成的。
使用多进程,其实就是由主进程,创建了多个子进程,每个子进程也都有对应要执行的任务
# 导入进程包import multiprocessing# 创建子进程对象并指定执行的任务sub_process = multiprocessing.Process (target=任务名)# 启动子进程执行任务sub_process.start()
import multiprocessingimport time# 任务1def task1(count): for i in range(count): print("任务1执行中..") time.sleep(0.2)# 任务2def task2(num): for i in range(num): print("任务2执行中..") time.sleep(0.2)if __name__ == '__main__': # group: 表示进程组,目前只能使用None # target: 表示执行的目标任务名(函数名、方法名) # name: 进程名称, 默认是Process-1, ..... # args: 以元组的方式给任务传入参数 # kwargs: 表示以字典方式传入参数 sub_process1 = multiprocessing.Process(target=task1, args=(5,)) sub_process2 = multiprocessing.Process(target=task2, kwargs={ "num": 3}) # 启动子进程执行对应的任务 sub_process1.start() sub_process2.start()
获取进程编号的目的是验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的。
获取进程编号的两种操作
线程是cpu调度的最小单位,是代码的真正执行者,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程
import threadingimport time# 任务1def task1(count): for i in range(count): print("任务1执行中..") time.sleep(0.2)# 任务2def task2(num): for i in range(num): print("任务2执行中..") time.sleep(0.2)if __name__ == '__main__': # group: 表示线程组,目前只能使用None # target: 表示执行的目标任务名(函数名、方法名) # name: 线程名称, 默认是Process-1, ..... # args: 以元组的方式给任务传入参数 # kwargs: 表示以字典方式传入参数 sub_thread1 = threading.Thread(target=task, args=(5,)) sub_thread2 = threading.Thread(target=task, kwargs={ "num": 3}) # 启动子线程执行对应的任务 sub_thread1.start() sub_thread2.start()
线程之间执行是无序的
线程之间共享全局变量
线程之间共享全局变量数据容易出现错误问题
主线程会等待所有的子线程执行结束再结束
设置守护主线程的方式:
sub_thread = threading.Thread(target=task, daemon=True)
sub_thread.setDaemon(True)
线程同步:子线程对象.join()
在编程中,为了避免共享数据出现错误,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
# 创建锁mutex = threading.Lock()# 上锁mutex.acquire()# 释放锁mutex.release()
注意点:
1.进程是操作系统资源分配的最小单位,线程是CPU调度的最小单位 2.线程依附于进程,没有进程就没有线程,一个进程默认提供一个线程(主线程),进程可以创建多个线程 3.进程不共享全局变量,同一个进程中的线程共享全局变量(资源竞争–>互斥锁,互斥锁可能产生死锁) 4.进程开销大但可以利用多核(并行),线程开销小但不能利用多核(并发)
1.多任务
概念:在同一时间段内执行多个任务好处呢就是 充分利用CPU,提高执行效率表现形式:1.并发 CPU在一段时间内交替执行多个任务. 2.并行 CPU在一段时间内只执行一个任务, 利用多个CPU完成执行多任务的功能
2.进程
概念:代码运行起来我们就称之为进程 是操作系统资源分配的最小单位 进程间不共享全局变量使用方式:1导包-import multiprocessing2创建子进程:无参数 p = multiprocessing.Process(target=目标函数) 元组方式传参:p = multiprocessing.Process(target=目标函数, args=(1,)) 字典方式传参:p = multiprocessing.Process(target=目标函数, kwargs={"num": 1})3.启动子进程:p.start()获取进程编号 获取当前进程编号:os.getpid() 获取父进程编号:os.getppid()主进程与子进程的执行顺序: 正常情况下主进程会等待子进程执行结束后再结束 设置守护主进程主进程代码执行完毕, 会关闭该子进程 销毁子进程 子进程对象.terminate() 等待子进程结束 子进程对象.join() 多进程执行是无序的
3.线程
概念:线程是CPU调度的最小单位 线程依附于进程,一个进程默认提供一个线程(主线程),进程可以创建多个线程 一个进程中的所有线程共享全局变量使用方式: 导包 import threading 创建子线程 无参数:t = threading.Thread(target=目标函数) 元组方式传参:t = threading.Thread(target=目标函数, args=(1,)) 字典方式传参:t = threading.Thread(target=目标函数, kwargs={"num": 1})启动子线程:t.start()主线程与子线程的执行顺序: 正常情况下主线程会等待子线程执行结束后再结束 设置守护主线程主线程代码执行完毕, 会关闭该子线程 (t = threading.Thread(target=目标函数, daemon=True)) (线程对象.setDaemon(True)) 多线程执行是无序的
4.进程线程比较
1.进程是操作系统资源分配的最小单位,线程是CPU调度的最小单位2.线程依附于进程,没有进程就没有线程,一个进程默认提供一个线程(主线程),进程可以创建多个线程3.进程不共享全局变量,同一个进程中的线程共享全局变量(资源竞争-->互斥锁,互斥锁可能产生死锁)4.进程开销大但可以利用多核(并行),线程开销小但不能利用多核(并发)
转载地址:http://evezi.baihongyu.com/