news 2025/12/24 20:32:19

Python线程与协程:多任务编程进阶指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python线程与协程:多任务编程进阶指南

一、引言

昨天讲了多任务和多进程,今天咱们接着将线程和协程.

二、多线程

1.线程的概念

线程是程序执行的最小单位, 实际上进程只负责分配资源 , 而利用这些资源执行程序的是线程 , 也就说进程是线程的容器 , 一个进程中最少有一个线程来负责执行程序 。同时线程自己不拥有系统资源,只需要一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。这就像通过一个QQ软件(一个进程)打开两个窗口(两个线程)跟两个人聊天一样 , 实现多任务的同时也节省了资源。

2.多线程完成多任务

① 导入线程模块 import threading ② 通过线程类创建线程对象 线程对象 = threading.Thread(target=任务名) ② 启动线程执行任务 线程对象.start()
参数名说明
target执行的目标任务名,这里指的是函数名(方法名)
name线程名,一般不用设置
group线程组,目前只能使用None

2.1线程创建与启动代码

import time import threading def music(): for i in range(3): print('听音乐...') time.sleep(0.2) def coding(): for i in range(3): print('敲代码...') time.sleep(0.2) if __name__ == '__main__': music_thread = threading.Thread(target=music) coding_thread = threading.Thread(target=coding) music_thread.start() coding_thread.start()

2.2线程执行带有参数的任务

参数名说明
args以元组的方式给执行任务传参
kwargs以字典方式给执行任务传参
import time import threading def music(num): for i in range(num): print('听音乐...') time.sleep(0.2) def coding(count): for i in range(count): print('敲代码...') time.sleep(0.2) if __name__ == '__main__': music_thread = threading.Thread(target=music, args=(3, )) coding_thread = threading.Thread(target=coding, kwargs={'count': 3}) music_thread.start() coding_thread.start()

3.主线程和子线程的结束顺序

import time import threading def work(): for i in range(10): print('work...') time.sleep(0.2) if __name__ == '__main__': # 创建子进程 work_thread = threading.Thread(target=work) # 启动线程 work_thread.start() # 延时1s time.sleep(1) print('主线程执行完毕')

设置守护线程方式一:

import time import threading def work(): for i in range(10): print('work...') time.sleep(0.2) if __name__ == '__main__': # 创建子线程并设置守护主线程 work_thread = threading.Thread(target=work, daemon=True) # 启动线程 work_thread.start() # 延时1s time.sleep(1) print('主线程执行完毕')

设置守护线程方式二:

import time import threading def work(): for i in range(10): print('work...') time.sleep(0.2) if __name__ == '__main__': # 创建子线程 work_thread = threading.Thread(target=work) # 设置守护主线程 work_thread.setDaemon(True) # 启动线程 work_thread.start() # 延时1s time.sleep(1) print('主线程执行完毕')

4.线程之间的执行顺序

线程之间的执行是无序的.

5.线程执行顺序验证

获取当前线程信息

# 通过current_thread方法获取线程对象 current_thread = threading.current_thread() # 通过current_thread对象可以知道线程的相关信息,例如被创建的顺序 print(current_thread)

线程间的执行顺序

import threading import time def get_info(): # 可以暂时先不加,查看效果 time.sleep(0.5) current_thread = threading.current_thread() print(current_thread) if __name__ == '__main__': # 创建子线程 for i in range(10): sub_thread = threading.Thread(target=get_info) sub_thread.start()

线程之间执行是无序的,是由CPU调度决定某个线程先执行的。

6.线程间共享全局变量

这点很好理解,前面我们讲了进程不共享全局变量的原因是不同进程都把最开始的全局变量复制了一份给自己,就好比孙悟空的分身,虽然是由孙悟空这个全局变量分出的,但每一个分身都可以看作是一个个体,对本体造不成任何影响.但是线程本质是同一个进程里的东西,就好比分身的四肢,四肢变化了,对本体肯定是有影响的,不知道这样形容大家有没有觉得更形象一点.

7.进程和线程的对比

7.1关系对比

① 线程是依附在进程里面的,没有进程就没有线程。

② 一个进程默认提供一条线程,进程可以创建多个线程。

7.2区别对比

① 进程之间不共享全局变量

② 线程之间共享全局变量

③ 创建进程的资源开销要比创建线程的资源开销要大

④ 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位

7.3优缺点对比

进程优缺点:

​ 优点:可以用多核

​ 缺点:资源开销大

线程优缺点

​ 优点:资源开销小

​ 缺点:不能使用多核

三、多协程

1.python中的生成器

根据程序设计者制定的规则循环生成数据,当条件不成立时则生成数据结束

数据不是一次性全部生成出来,而是使用一个,再生成一个,可以节约大量的内存。

创建生成器的方式

① 生成器推导式

生成器推导式与列表推导式类似,只不过生成器推导式使用小括号。

# 创建生成器 my_generator = (i * 2 for i in range(5)) print(my_generator) # next获取生成器下一个值 # value = next(my_generator) # print(value) # 遍历生成器 for value in my_generator: print(value)

生成器的相关函数:

next 函数获取生成器中的下一个值

for 循环遍历生成器中的每一个值

② yield 关键字

yield 关键字生成器的特征:在def函数中具有yield关键字

def generator(n): for i in range(n): print('开始生成...') yield i print('完成一次...') g = generator(5) print(next(g)) print(next(g)) print(next(g)) print(next(g)) print(next(g)) -----> 正常 print(next(g)) -----> 报错

注意点:

① 代码执行到 yield 会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行

② 生成器如果把数据生成完成,再次获取生成器中的下一个数据会抛出一个StopIteration 异常,表示停止迭代异常

③ while 循环内部没有处理异常操作,需要手动添加处理异常操作

④ for 循环内部自动处理了停止迭代异常,使用起来更加方便,推荐大家使用。

yield关键字和return关键字

如果不太好理解yield,可以先把yield当作return的同胞兄弟来看,他们都在函数中使用,并履行着返回某种结果的职责。

这两者的区别是:

有return的函数直接返回所有结果,程序终止不再运行,并销毁局部变量;

def example(): x = 1 return x example = example() print(example)

而有yield的函数则返回一个可迭代的 generator(生成器)对象,你可以使用for循环或者调用next()方法遍历生成器对象来提取结果。

def example(): x = 1 y = 10 while x < y: yield x x += 1 example = example() print(example)

2.Python中的协程

Python 的协程实现确实是从生成器发展而来的。

特性生成器 (Generator)协程 (Coroutine)
主要目的生成值序列执行异步任务
控制流单向:调用者 → 生成器双向:调用者 ↔ 协程
数据流向数据向外流动(产出值)数据双向流动(发送和接收)
调度由调用者驱动由事件循环调度
关键字yieldasync,await
类型GeneratorCoroutine

Python 协程是由async def定义的异步函数,它是一种实现了Awaitable协议的状态保持执行单元。其执行由事件循环调度,通过await表达式主动挂起,将控制权交还调度器,实现协作式并发

协程三要素:

  1. 函数前加 async
  2. 等待处加 await
  3. 启动用 asyncio.run

使用三步骤:

  1. 定义 async 函数
  2. 用 create_task 创建任务
  3. 用 await 等待结果

3.进程、线程、协程最简单记忆方法

  • 协程:单线程魔术师,手里抛接多个球(I/O等待时换件事做)
  • 线程:多个魔术师,但只有一个能表演(GIL限制)
  • 进程:多个魔术师,各自独立表演 (完全独立)

四、结语

多任务变成这块就告一段落了,后面会继续讲正则表达式的相关概念.

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/20 9:40:34

创客匠人峰会后实战复盘:智能体系统落地的 3 大误区与破局之道

在 AI 技术深度渗透知识变现领域的当下&#xff0c;"智能体重构业务" 已成为行业共识&#xff0c;但多数创始人 IP 在落地过程中陷入误区 —— 要么盲目跟风开发多个智能体却收效甚微&#xff0c;要么脱离用户需求导致系统沦为摆设。2025 年 11 月22日-25日由创客匠人…

作者头像 李华
网站建设 2025/12/20 16:01:53

投影机选型核心技术解析:4大参数原理+实操选型指南

投影机选型核心技术解析&#xff1a;4大参数原理实操选型指南在会议室演示、教室教学、家庭影音、大型展厅等场景中&#xff0c;投影机的性能表现直接取决于核心参数的技术合理性&#xff0c;但行业内普遍存在“参数虚标”“技术概念模糊”等问题&#xff0c;导致选型偏差。本文…

作者头像 李华
网站建设 2025/12/17 1:52:08

场地扫地车是什么?主要有哪几种类型及其特点?

场地扫地车定义与基本特征分析场地扫地车是一种专门用于清洁各种场所的设备&#xff0c;设计旨在提高清扫效率。这类车辆通常具备强大的吸尘和喷水功能&#xff0c;能够快速清除地面上的灰尘、落叶、小石子及其他固体垃圾。它们适合多种硬质地面&#xff0c;如水泥、沥青和砖石…

作者头像 李华