Python【协程(Coroutine)和线程的关系】

        协程(Coroutine)和线程都是实现并发编程的技术,但它们在实现方式、使用场景和性能上有显著区别。理解它们的关系与差异有助于在实际应用中选择合适的并发模型,以下是它们的核心关系与对比分析:

一、核心关系

  1. 互补关系
    协程和线程可以结合使用(如一个线程内运行多个协程,或多个线程各自运行协程)。

  2. 替代关系
    在I/O密集型场景中,协程常被用来替代线程以减少资源开销。

  3. 层次关系
    协程是用户态的"轻量级线程",线程是操作系统调度的基本单位。

生活化的比喻来解释:快餐店点餐

线程版餐厅(传统多线程)

  • 每个顾客(任务)配一个专属服务员(线程)

  • 服务员A带顾客1点餐 → 等厨师做汉堡(线程阻塞)

  • 服务员B带顾客2点餐 → 等可乐机打饮料(线程阻塞)

  • 问题
    1个顾客发呆时,他的服务员只能干等着,不能服务其他人
    雇100个服务员(线程)成本太高(内存爆炸)

协程版餐厅(异步协程)

  • 1个超级服务员(事件循环)服务所有顾客

  • 带顾客1点汉堡 → 记下"等3分钟" → 立刻服务顾客2
    顾客2要可乐 → 记下"等1分钟" → 检查顾客1的汉堡好了没...

  • 优势
    1个服务员搞定全场!谁的食物好了就继续服务谁

二、关键对比

特性协程 (Coroutine)线程 (Thread)
调度方用户程序控制(事件循环)操作系统内核调度
切换成本极低(仅寄存器保存)高(需要内核态/用户态切换)
内存占用通常KB级通常MB级(默认栈大小)
并发数量轻松支持数万级通常数百到数千
并行能力单线程内并发,需多线程实现并行可利用多核CPU
阻塞影响一个协程阻塞会阻塞整个事件循环一个线程阻塞不影响其他线程
适用场景I/O密集型(网络请求、文件读写等)CPU密集型或混合型任务
典型实现Python asyncio、Go goroutinePython threading、Java线程

生活化的比喻来解释核心区别总结:

线程协程
员工雇佣大量服务员1个超人服务员
成本工资高(内存占用大)工资低(内存占用小)
效率经常站着等(阻塞)永远在忙(非阻塞)
规模最多几百人能接待数万人

三、技术原理差异

1. 线程的实现

  • 内核调度:线程切换需要CPU从用户态切换到内核态

  • 抢占式调度:操作系统决定何时切换线程

  • 同步问题:必须使用锁等机制解决资源竞争

    # Python线程示例
    import threadingdef task():print("Thread running")thread = threading.Thread(target=task)
    thread.start()

2. 协程的实现

  • 用户态调度:通过事件循环(Event Loop)管理

  • 协作式调度:协程主动让出控制权(通过await

  • 单线程内并发:通过状态保存/恢复实现

    # Python协程示例
    import asyncioasync def task():print("Coroutine running")asyncio.run(task())

四、典型协作模式

1. 单线程+多协程(经典异步模型)

async def fetch(url):print(f"Start fetching {url}")await asyncio.sleep(1)  # 模拟I/Oprint(f"Finished {url}")async def main():await asyncio.gather(fetch("url1"),fetch("url2")  # 两个协程并发执行)asyncio.run(main())

输出:

Start fetching url1
Start fetching url2
Finished url1
Finished url2

2. 多线程+每线程多协程(高并发服务)

async def handle_connection(reader, writer):data = await reader.read(100)writer.write(data)await writer.drain()async def thread_main():server = await asyncio.start_server(handle_connection, '127.0.0.1', 8888)async with server:await server.serve_forever()# 每个线程运行独立的事件循环
def start_thread():asyncio.run(thread_main())threads = [threading.Thread(target=start_thread) for _ in range(4)]
for t in threads: t.start()

五、如何选择?

使用协程当:

  • 需要高并发I/O操作(如Web服务器)

  • 希望减少内存开销

  • 需要精细控制执行流程

  • 避免锁的复杂性(单线程内协程无需锁)

    使用线程当:

  • 需要利用多核CPU(计算密集型任务)

  • 调用阻塞式第三方库(如某些数据库驱动)

  • 需要操作系统级别的并行

    混合使用建议:

  • 用协程处理I/O密集型任务

  • 用线程池处理CPU密集型任务

  • 例如:FastAPI等框架使用线程池+协程的混合模式

现实场景
协程:适合网络请求(比如同时爬1万个网页)
→ 就像服务员在等网页响应时,可以先去处理其他请求

线程:适合计算任务(比如视频转码)
→ 必须真的用多个厨师(CPU核心)同时干活

六、性能对比实验

import asyncio
import threading
import time# 协程版本
async def coroutine_task(id):await asyncio.sleep(1)async def coroutine_main():tasks = [coroutine_task(i) for i in range(1000)]await asyncio.gather(*tasks)# 线程版本
def thread_task(id):time.sleep(1)def thread_main():threads = []for i in range(1000):t = threading.Thread(target=thread_task, args=(i,))t.start()threads.append(t)for t in threads: t.join()# 测试执行
start = time.time()
asyncio.run(coroutine_main())
print(f"Coroutines: {time.time()-start:.2f}s")start = time.time()
thread_main()
print(f"Threads: {time.time()-start:.2f}s")

输出

Coroutines: 1.02s  # 1000个协程并发
Threads: 1.05s    # 1000个线程竞争资源

七、现代发展趋势

  1. 协程成为主流:Go的goroutine、Python asyncio、Rust tokio等

  2. 线程角色转变:更多作为协程的底层支撑(如CPU密集型任务)

  3. 混合编程模型

    • 使用协程处理高并发I/O

    • 使用线程池处理阻塞/计算任务

    • 使用多进程利用多核

理解两者的关系和差异,能帮助开发者根据场景选择最佳并发方案。

八、终极结论

  • 需要等别人(IO操作)时 → 用协程(省钱高效)

  • 需要真干活(CPU计算)时 → 用线程/进程(利用多核)

就像餐厅:
人多但都只是点单 → 1个超人服务员(协程)
真的要做100道菜 → 必须雇多个厨师(线程/进程)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80487.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Springboot——Redis的使用

在当今的软件开发领域,缓存技术是提升应用性能的关键手段之一。Redis 作为一款高性能的键值对存储数据库,凭借其出色的读写速度和丰富的数据结构,在缓存场景中得到了广泛应用。Spring Boot 作为一款简化 Spring 应用开发的框架,与…

BEVPoolv2:A Cutting-edge Implementation of BEVDet Toward Deployment

背景 该论文是在BEVDet的基础上进行了一个调整优化,传统的方法是将特征图与深度预测进行外积得到视椎特征图,再将它与预处理好的体素索引结合,将每个视椎特征分类到每个voxel中进行累加和的操作。BEVFusion与BEVDepth等方法是避免了累加和&a…

蓝桥杯常考的找规律题

目录 灵感来源: B站视频链接: 找规律题具有什么样的特点: 报数游戏(Java组): 题目描述: 题目链接: 思路详解: 代码详解: 阶乘求和(Java组…

使用ffmpeg 将图片合成为视频,填充模糊背景,并添加两段音乐

1.输入3张图片,每张播放一次,播放两秒,视频分辨率设置为1920:1080,每张图片前0.3秒淡入,后0.3秒淡出,图片宽高比不变,用白色填充空白区域 ffmpeg -loop 1 -t 2 -i "img1.jpg" \-loop 1 -t 2 -i "img2.jpg" \-loop 1 -t 2 -i "img3.jpg" \-filte…

PostgreSQL技术内幕29:事件触发器tag原理解析

文章目录 0.简介1.概念说明2.tag的生成和存储2.1 tag合法性校验2.2 内存中存储2.3 持久化存储 3.tag的触发 0.简介 在上一篇文章中中,我们介绍了PG中的两种触发器,即适合于DML的普通触发器和对于DDL的事件触发器,其中事件触发器与常规的 DML…

mysql 导入很慢,如何解决

精选 原创 码出财富2025-04-14 17:35:14博主文章分类:数据库©著作权 文章标签mysql数据库用户名文章分类MySQL数据库yyds干货盘点阅读数184 导入大型 SQL 文件到 MySQL 数据库时,速度可能会受到影响。以下是一些优化方法和建议,帮助你…

多物理场耦合低温等离子体装置求解器PASSKEy2

文章目录 PASSKEy2简介PASSKEY2计算流程PASSKEy2 中求解的物理方程电路模型等离子体模型燃烧模型 PASSKEy2的使用 PASSKEy2简介 PASSKEy2 是在 PASSKEy1 的基础上重新编写的等离子体数值模拟程序。 相较于 PASSKEy1, PASSKEy2 在具备解决低温等离子体模拟问题的能力…

保姆级zabbix监控jmx、数据库和网络监控(SNMP)

前言 在当今数字化时代,企业IT基础设施的稳定性与性能直接关系到业务连续性和用户体验。随着系统复杂性的不断增加,单一维度的监控已难以满足全面运维需求。Zabbix作为一款功能强大的开源监控解决方案,通过整合JMX(Java Manageme…

复杂地形越野机器人导航新突破!VERTIFORMER:数据高效多任务Transformer助力越野机器人移动导航

作者: Mohammad Nazeri 1 ^{1} 1, Anuj Pokhrel 1 ^{1} 1, Alexandyr Card 1 ^{1} 1, Aniket Datar 1 ^{1} 1, Garrett Warnell 2 , 3 ^{2,3} 2,3, Xuesu Xiao 1 ^{1} 1单位: 1 ^{1} 1乔治梅森大学计算机科学系, 2 ^{2} 2美国陆军研究实验室&…

SharpMap与TerraLib:C#与C++开源GIS库

大家好,今天为大家介绍的软件是SharpMap:一款专为了C#(.NET)环境设计的开源地图和空间数据处理库;TerraLib:一款由C编写、支持多种数据库的开源的GIS软件库。 下面,我们将从两个开源软件的主要…

音视频学习 - MP3格式

环境 JDK 13 IDEA Build #IC-243.26053.27, built on March 16, 2025 Demo MP3Parser MP3 MP3全称为MPEG Audio Layer 3,它是一种高效的计算机音频编码方案,它以较大的压缩比将音频文件转换成较小的扩展名为.mp3的文件,基本保持源文件的音…

Unity中数据和资源加密(异或加密,AES加密,MD5加密)

在项目开发中,始终会涉及到的一个问题,就是信息安全,在调用接口,或者加载的资源,都会涉及安全问题,因此就出现了各种各样的加密方式。 常见的也是目前用的最广的加密方式,分别是:DE…

部署本地deepseek并在调用的详细步骤以及解决一些可能出现的问题(Windows,Linux, WSL)

打开Ollama官网:https://ollama.com/ 直接下载Ollama并且安装好Ollama、这时候就能看到app里多了个ollama,但是我们不用打开它 打开Windows Powershell: ollama run deepseek-r1:1.5b 7b 8b 14b 32b 70b 根据自己的电脑配置和需求更换不同的…

【KWDB 创作者计划】_嵌入式硬件篇---寄存器与存储器截断与溢出

文章目录 前言一、寄存器与存储器1. 定义与基本概念寄存器(Register)位置功能特点存储器(Memory)位置功能特点2. 关键区别3. 层级关系与协作存储层次结构协作示例4. 为什么需要寄存器性能优化指令支持减少总线竞争5. 其他寄存器类型专用寄存器程序计数器(PC)栈指针(SP)…

小白自学python第二天

学习python的第二天 一、判断语句 1、布尔类型和比较运算符 1、布尔类型 表示现实生活中的逻辑,真(True,用数字1表示)和假(False,用数字0表示) 2、布尔类型变量的定义 变量的名称 布尔类…

linux基础操作1------(文件命令)

一.前言 我们本章开始讲解linux,我们对于linux得有重要的认识,比如项目部署等等,都会用到linux,今天我们就开始linux的学习,我们需要准备的工具有vmware和xshell,而这里我就不教大家虚拟机的安装以及xshel…

编码问题整合

一、windows系统编码 查看编码命令:chcp - 936 GBK - 65001 UTF-8 - 437 英文修改系统编码 1、控制面板修改 需管理员权限-Windows 10/11进入 控制面板 > 区域 > 管理 > 更改系统区域设置勾选 Beta版: 使用Unicode UTF-8提供全球语言支持 → 重启生效修…

如何配置Spark

1.上传spark安装包到某一台机器(自己在finaShell上的机器)。 2.解压。 把第一步上传的安装包解压到/opt/module下(也可以自己决定解压到哪里)。对应的命令是:tar -zxvf 安装包 -C /opt/module 3.重命名。进入/opt/mo…

Redis 完整配置模板

一、基础连接配置(单机模式) 基础参数(适用Spring Boot) spring:redis:host: 127.0.0.1port: 6379password: your_passworddatabase: 0 # 默认DB索引timeout: 2000ms # 全局操作超时时间二、连接池参数(通用核心配…

边界凸台建模与实例

文章目录 边界凸台特征耳机案例瓶子 边界凸台特征 两侧对称拉伸最上面的圆柱 同过两点一基准面画草图,在基准面上画椭圆 隐藏无关的实体和草图,以便椭圆的端点能与线给穿透约束,下面的点与下面的线也给穿透,短轴长给35&#xff08…