news 2026/7/5 8:13:13

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进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/4 6:05:49

基于SpringBoot的校园二手书交易平台的设计与实现

基于SpringBoot的校园二手书平台的设计与实现 学生姓名&#xff1a;张三 班级&#xff1a;1882062 指导老师&#xff1a;李四 摘要&#xff1a;科学技术的发展推动各行各业的变革&#xff0c;以信息技术和互联网为代表的新型技术革命正在推动社会各行各业的发展。信息技术的…

作者头像 李华
网站建设 2026/7/4 18:52:15

数据结构与算法--007三数之和(medium)

15. 三数之和 - 力扣&#xff08;LeetCode&#xff09; 算法思路&#xff1a; 去重的两种方法&#xff1a; 方法一(暴力解法)&#xff1a; Arrays.asList() 是 Java 中的一个方法&#xff0c;它用于将数组或集合转换为一个 固定大小的 列表&#xff08;List&#xff09;。 …

作者头像 李华
网站建设 2026/6/27 0:49:38

C++ 模板初阶:泛型编程的入门指南

在 C 编程中&#xff0c;我们经常会遇到这样的场景&#xff1a;需要实现功能完全相同&#xff0c;但处理数据类型不同的函数。比如交换两个整数、交换两个浮点数、交换两个字符的函数。最直接的想法是用函数重载&#xff0c;但这种方式的弊端显而易见 —— 代码复用率低、可维护…

作者头像 李华
网站建设 2026/7/2 9:17:46

基于Java实现优雅关闭的规范化方案设计与实现

文章目录前言背景初步调研实现思路方案核心需求分析技术方案对比实现思路初步功能设计关键设计决策实现步骤与代码第一步&#xff1a;核心关闭管理器实现第二步&#xff1a;集成到钉钉客户端管理器&#xff08;注册关闭&#xff09;总结说明资料获取前言 博主介绍&#xff1a;…

作者头像 李华
网站建设 2026/7/1 23:06:19

时序数据战场巅峰对决:金仓数据库 VS InfluxDB深度解析

引言 在物联网、工业互联网与运维监控领域&#xff0c;时序数据正以前所未有的速度迅猛增长。海量设备持续产生的数据流&#xff0c;对数据库提出了双重核心要求&#xff1a;既要支撑高速数据写入&#xff0c;又要实现快速复杂分析。长期以来&#xff0c;InfluxDB凭借时序领域的…

作者头像 李华