Python——进程、线程、协程、多进程 队列、多线程(个人向)

it2026-10-08  0

一、进程 线程 协程

1.进程

一个运行的程序就是一个进程,没有运行的代码叫做程序。 进程是系统分配资源的最小单位,进程拥有自己独立的内存空间 相对独立,所以进程间的数据不共享,开销大。

2.线程

CPU调度执行的最小单元(程序真正执行的时候调用的是线程),也叫执行路径,依赖于进程存在, 一个进程最少存在一个线程,叫做主线程 而多个线程共享进程内存资源(数据共享,共享全局变量),从而极大地提高了程序的运行效率。

一个进程中可以有一个或多个线程。一个线程只属于一个进程。 一个进程中的多个线程是一种竞争关系。

3.协程

协程类似于线程,基于线程,但是协程允许一个执行过程A中断,转而执行过程B,然后再执行过程A 优势: 协程理论上数量有无限个, 没有线程之间的切换动作,所以比较快; 没有锁机制, 因为所有协程都在一个线程中.

是一种用户态的轻量级线程,协程的调度完全由用户控制。 协程拥有自己的寄存器上下文和栈。 协程调度切换时,将寄存器上下文和栈保存到其他地方, 在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销, 可以不加锁的访问全局变量,所以上下文的切换非常快。

二、多进程 多线程

1.多进程

(并行:两个人每个人同时处理一个任务)

多进程模块:multiprocessing ,CPU密集型、IO计算型可以使用多进程 模块导入:from multiprocessing import Process Process创建进程

Process([group [, target [, name [, args [, kwargs]]]]])

target如果传递了函数的引用,可以让这个子进程就执行函数中的代码target如果传递了函数的引用,可以让这个子进程就执行函数中的代码args给 target 指定的函数传递的参数,以元组的形式进行传递kwargs给 target 指定的函数传递参数,以字典的形式进行传递name给进程设定一个名字,可以省略group指定进程组,大多数情况下用不到

常用方法:

start()启动子进程实例(创建子进程)is_alive()判断进程子进程是否还在活着terminate()不管任务是否完成,立即终止子进程Process 创建的实例对象的常用属性name当前进程的别名,默认为 Process-N,N 为从 1 开始递增的整数pid当前进程的 pid(进程号)

一个进程的,生命周期中的几种状态: 出生: 就绪:start() 执行: 阻塞:sleep() / IO操作 死亡:正常结束 / 异常结束

import time from multiprocessing import Process def download1(name): for i in range(1, 6): print(f'{name}下载进度:{i * 20}%') time.sleep(1) def download2(name): for i in range(1, 6): print(f'{name}下载进度:{i * 20}%') time.sleep(1) if __name__ == '__main__': # 创建进程 p1 p2 p1 = Process(target=download1, args=("图片",)) p2 = Process(target=download2, args=("音乐",)) # 开启进程 p1.start() p2.start() """ 音乐下载进度:20% 图片下载进度:20% 图片下载进度:40% 音乐下载进度:40% 图片下载进度:60%音乐下载进度:60% 图片下载进度:80% 音乐下载进度:80% 图片下载进度:100%音乐下载进度:100% """

程序理解:

主进程从main()开始执行 → p1.start()时,创建一个子进程 pi子进程中的代码与主进程相同,只是程序执行的开始是download1函数 → 主进程执行到 p2.start()时,同样赋值一份主进程代码从download2函数开始执行。

用进程方式来实现多任务耗费资源较大,因为一个进程就需要使用一份系统资源 ——————————————————————

实现不同进程之间的通信

使用队列Queue:多进程间的共享队列 (锁 信号量处理系统已具备 ) 模块导入:from multiprocessing import Queue

q = Queue(3) # 3表示只能存放3个数据 参数 :maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。 返回值q 是队列对象

工作机制:FIFO 先进先出 常用方法

qsize()对长put()向队列中存放数据,若队列已满,此方法将阻塞至有空间可用为止put_nowait()不考虑其他直接入队get()返回队列中一个数据full()是否对满,若已满返回Tureempty()是否对空,若为空返回Ture

———————————————————————

进程池

作用:多任务并行执行时,通过确定并行执行进程数量,管理任务执行过程

如若有100个任务需要处理,可直接使用Process创建100个子进程实现,但资源比较浪费。 也可以创建指定个数个子进程,如只创建10个子进程,让10个子进程重复执行任务这样就节约了资源

模块导入:from multiprocessing import Pool

同步::任务1 2,先执行任务1,在执行任务2 apply(func[, args[, kwds]]) 同步操作,它会阻塞,直到结果就绪

异步::任务1 2,1 2两个任务之间互不影响,可同时执行 apply_async(func[, args[, kwds[, callback[, error_callback]]]]) 异步操作,不会阻塞,可以指定回调函数 异步任务中,可以指定回调函数,回调函数可以接受参数, 参数的实际值来自目标任务函数的返回值

“”“ 模拟实现一部电视剧的下载 “”” import random import time from multiprocessing import Pool teleplay = [f'西部世界第{i}集' for i in range(1, 20)] def download(name): for i in range(1, 11): print(f'{name}下载:{i * 10}%') time.sleep(random.random()) return name def alert(name): print(f'{name}下载完成!') """ 1 创建进程池 2 将下载任务都放入进程池中 3 关闭进程池 close 4 让进程池阻塞其他任务(让我先来) join """ if __name__ == '__main__': pool = Pool() #由于没有设置数量 默认为 os.cup_count 系统最优 for s in teleplay: pool.apply_async(func=download, args=(s,), callback=alert) #异步 pool.close() pool.join() """ 西部世界第1集下载:10% 西部世界第2集下载:10% 西部世界第3集下载:10% 西部世界第4集下载:10% 西部世界第4集下载:20%西部世界第1集下载:20% 西部世界第3集下载:20% 西部世界第4集下载:30% ... """ pool.apply(func=download, args=(s,)) #同步 """ 西部世界第1集下载:10% 西部世界第1集下载:20% 西部世界第1集下载:30% 西部世界第1集下载:40% ... """

——————————————————————————————

2.多线程

(并发:一个人处理两个任务,处理过程中,可能会出现两个任务交替的情况)

多线程模块:threading,IO密集型可以使用多线程 模块导入:from threading import Thread Thread创建线程

Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None))

常用方法:

join()当前线程执行完后其他线程才会继续执行(可以阻塞其他的,让我自己先来)setDaemon()当前线程设置成守护线程,主线程结束,子线程无论是否执行完毕,直接结束 ;注意:需要在子线程开启之前设置成守护线程,否则无效。threading.currentThread()返回当前的线程变量threading.enumerate()返回一个包含正在运行的线程list,正在运行是指线程启动后、结束前,不包括启动前和终止后的线程threading.activeCount()与len(threading.enumerate) 相同;返回正在运行的线程数量 import time from threading import Thread def download1(name): for i in range(1, 6): print(f'{name}下载进度:{i * 20}%') time.sleep(1) def download2(name): for i in range(1, 6): print(f'{name}下载进度:{i * 20}%') time.sleep(1) if __name__ == '__main__': # 1.创建线程 t1 = Thread(target=download1, args=('音乐',)) t2 = Thread(target=download2, args=('图片',)) # 2.开启线程 t1.start() # t1.join() t2.setDaemon(True) #t2 为守护线程 t2.start() “”“ 下载任务1进行:20% 下载任务2进行:10% 下载任务2进行:20%下载任务1进行:40% 下载任务2进行:30%下载任务1进行:60% 下载任务1进行:80%下载任务2进行:40% 下载任务1进行:100%下载任务2进行:50% ”“”

线程的创建方式:

基于类继承的方式实现自定义的线程类: threading.Thread

import threading import time class Mythread(threading.Thread): def __init__(self, num): #必须要处理父类init方法 super().__init__() self.num = num def run(self): for i in range(self.num): print(f'i={i}, {threading.currentThread()}') time.sleep(1) #在run里添加调用其他函数 如 self.login() def login(self): ... if __name__ == '__main__': # 创建一个实例对象,只能有一个线程,一个线程同一时刻只能执行一个函数 t1 = Mythread(5) #正常方式传递参数 t1.start()
最新回复(0)