当前位置: 首页 > news >正文

Python中 async/await 和传统多线程的区别?

async/await 这套东西现在火得很,面试官特别喜欢拿它和老牌的多线程做对比。这两个都涉及到并发,但骨子里的哲学完全不一样。搞混了,很容易在项目中埋坑。

来,咱们把这个掰扯清楚。


这问题想考你什么?

面试官问这个,其实是想一石二鸟:

第一,他想知道你懂不懂并发模型的差异。多线程是操作系统层面的,靠CPU来回切换;async/await是应用程序层面的,靠一个叫“事件循环”的东西自己调度。说白了,就是看你知不知道“活儿是怎么被安排的”。

第二,也是更重要的,他想看你能不能根据具体场景,做出最合理的技术选型。什么时候该上多线程,什么时候用异步才是最优解?这直接体现了你作为高级工程师的架构判断力。

是什么?

咱们还用比喻来说,这样好理解。

  • 传统多线程:就像一个银行开了好几个柜台窗口(线程)。每个窗口都能独立完整地接待一个客户(任务)。如果某个客户需要填一张复杂的表,那个柜台就得等着,直到客户填完。虽然这个柜台在等,但其他柜台还能继续接待别的客户。这是靠增加人手(线程)来并行处理任务

  • Async/Await (异步):这更像一个超级厉害的银行王牌柜员,他只开一个窗口。当第一个客户来办业务,发现需要回去拿身份证时,这个王牌柜员不会傻等,他会立刻跟客户说:“你先去拿,拿好了再回来,我先给下一个人办。” 然后他马上开始处理第二个客户的业务。等第一个客户拿着身份证回来了,他再找个空隙把剩下的业务办完。

看到了吗?这个王牌柜员(单线程)从来没有闲着,他通过在任务的等待时间(比如等网络、等IO)里来回切换,一个人就搞定了多个客户的活儿。这是靠优化工作流程来处理并发任务

怎么用?

空口无凭,上代码最实际。咱们就用一个最常见的场景:同时从好几个网站上抓取首页内容。

1. 传统多线程版
import threading
import requests
import time# 准备要抓取的URL
URLS = ['https://www.google.com','https://www.python.org','https://www.github.com',
]def fetch_url(url):"""一个简单的阻塞函数,用于获取URL内容"""try:# requests.get() 是一个阻塞操作,会等待网络IOresp = requests.get(url)print(f"Fetched {url}, got {len(resp.content)} bytes.")except Exception as e:print(f"Error fetching {url}: {e}")def multithreading_test():"""使用多线程来并发抓取"""print("--- 多线程版本 ---")start_time = time.time()threads = []for url in URLS:# 为每个URL创建一个线程thread = threading.Thread(target=fetch_url, args=(url,))threads.append(thread)thread.start() # 启动线程for thread in threads:thread.join() # 等待所有线程执行完毕end_time = time.time()print(f"多线程耗时: {end_time - start_time:.2f} 秒\n")
2. Async/Await 异步版

要用异步,咱们得用支持异步的网络库,比如 aiohttp。

import asyncio
import aiohttp
import time# URLS列表和上面一样async def fetch_url_async(session, url):"""一个异步的协程函数,用于获取URL内容"""try:# session.get() 是一个非阻塞操作,遇到IO会把控制权交出去async with session.get(url) as response:content = await response.read()print(f"Fetched {url}, got {len(content)} bytes.")except Exception as e:print(f"Error fetching {url}: {e}")async def async_test():"""使用async/await来并发抓取"""print("--- 异步版本 ---")start_time = time.time()async with aiohttp.ClientSession() as session:# 创建一个任务列表tasks = [fetch_url_async(session, url) for url in URLS]# asyncio.gather() 会并发执行所有任务await asyncio.gather(*tasks)end_time = time.time()print(f"异步耗时: {end_time - start_time:.2f} 秒")if __name__ == "__main__":multithreading_test()# 在Jupyter或某些环境里,直接运行 await async_test()# 在普通的.py文件里,需要用 asyncio.run()asyncio.run(async_test())

你跑一下这两个版本的代码,会发现它们完成任务的总时间都远小于一个一个按顺序抓取的时间,说明都实现了并发。但它们的实现方式和代码风格,差别就很大了。

用在哪?

这才是重点,什么时候用哪个?

  • async/await 的主场:高并发IO密集型场景。
    我之前做过一个直播间的弹幕推送服务。一个热门直播间可能有几万甚至几十万用户同时在线,每个用户都维持着一个WebSocket长连接。如果给每个用户都开一个线程,那服务器的内存和线程调度开销早就爆了。

    这种场景就是异步的天下。我们用asyncio配合websockets库,一个单线程的进程就能轻松维护成千上万个几乎同时处于“等待消息”状态的连接。CPU占用极低,资源开销小,性能非常好。核心思想是:连接虽多,但真正有数据传输的瞬间很少,异步模型正好能完美应对这种“大量等待,少量计算”的模式。

  • 多线程的用武之地:处理既有IO又有少量CPU计算,或与阻塞库集成的场景。
    虽然异步很香,但它有个“传染性”,你用的所有库都得是支持异步的。有时候,你不得不跟一些老的、只提供阻塞接口的库打交道,比如某个数据库的官方驱动、某个硬件的SDK等。

    我之前遇到一个任务,需要从一个老式的文件服务器上下载数据(IO密集),下载后还要立刻对压缩包做个解压和校验(有少量CPU计算)。这种情况下,硬掰成异步很痛苦,因为解压这个操作是同步阻塞的。用多线程就非常自然,每个线程负责“下载->解压->校验”这一条龙服务,代码写起来简单直接,也能很好地利用并发。

有什么坑?

聊聊那些让你头疼的坑,这能让你在面试官面前显得更有经验。

  1. 异步的“毒性”:async一旦用上,基本上你整个调用链都得是async。在一个async def函数里,你不能直接调用一个普通的、会阻塞的函数(比如requests.get()),否则它会把整个事件循环都给卡死,所有并发任务都得等它一个,异步的优势就荡然无存了。你必须使用这个库的异步版本(比如aiohttp)。这是新手最容易犯的错。

  2. 多线程的“噩梦”——数据共享和锁:多线程最让人头疼的就是多个线程同时修改一个共享数据,比如一个全局变量。这会引发所谓的“竞态条件”,导致数据错乱。为了解决这个问题,你得引入各种锁(threading.Lock),但一旦锁用不好,又可能导致“死锁”——两个线程互相等着对方释放锁,结果大家一起卡死。调试这种问题非常痛苦。而单线程的异步模型基本没有这个问题,因为不存在多个线程抢占数据的情况。

  3. 调试难度:异步代码的调用栈不像同步代码那么直观,出错了有时候很难追溯根源。多线程的bug,特别是跟锁相关的,复现起来就更看运气了。两者在调试上都比简单的同步代码要复杂。

  4. 别拿它们干CPU密集型的活儿:这一点跟我们上次聊GIL时一样。无论是多线程还是asyncio,因为都受GIL的限制,它们在单个Python进程里都无法真正利用CPU多核来做并行计算。遇到这种场景,请老老实实上多进程(multiprocessing)。

总结一下,多线程和异步是解决并发问题的两种不同工具,理解它们的原理和边界,才能在合适的场景亮出合适的家伙。能跟面试官把这几点聊透,他绝对会觉得你是个明白人。

http://www.dtcms.com/a/511819.html

相关文章:

  • 手机网站建设信息wordpress addaction
  • 网站建设接单渠道上海定制建设网站
  • 亮相IROS 2025:傅利叶以开源协同推动具身智能技术迭代
  • Linux中的`fork`函数详解:深入解析
  • 嘉兴建设企业网站wordpress破解key
  • 自然语言处理实战——基于感知机模型的中文文本情感分类
  • 接到一个需求,怎么做性能分析,以及性能优化过程
  • 网站横幅背景图片企业网站不备案会怎么样
  • 网站建设用户需求调查物流公司
  • Leetcode+Java+图论II
  • git空目录处理
  • 自动化办公:用Python操作Excel、Word和PDF
  • 前端V0介绍(Vercel推出的AI前端生成工具)
  • 从 “对话” 到 “证书”:零知识证明的魔法工具箱 —— 让隐私验证走进普通人的数字生活
  • 培训类网站开发做网站需要买服务器
  • 对称树结构:原理、应用与Python实现
  • 4.4数组的基本操作
  • 湘潭网站建设优等磐石网络遨游建站
  • [go 面试] 前端请求到后端API的中间件流程解析
  • Ethernaut Level 13: Gatekeeper One - Gas计算与类型转换
  • 飞凌嵌入式ElfBoard-常用的网络服务的搭建之TFTP服务搭建
  • mybatis-plus的insertBatchSomeColumn方法实现批量插入
  • 上海传媒公司艺人seo项目优化案例分析文档
  • 【论文阅读】DiffusionDrive:截断扩散模型用于端到端自动驾驶
  • 解读Time Model Statistics中的PL/SQL 和 SQL执行时间
  • DDD(一)认识领域驱动设计(DDD的概念、主要架构模型)
  • Spring Boot集成Spring Integration全解析
  • MCP功能与架构详解
  • Spring Boot优雅关闭全解析
  • 授权登录网站怎么做网站源码怎么做