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

本文详细讲解了进程、线程和协程的区别与应用,涉及多进程的multiprocessing模块、多线程的threading模块,以及协程的高效特性,包括进程池和线程池的使用。重点展示了如何在实际场景中利用这些技术进行任务并行和资源优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、进程 线程 协程

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()是否对满,若已满返回Ture
empty()是否对空,若为空返回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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值