系列索引:菜菜的并发编程笔记 | Python并发编程详解(持续更新~)
一、思维导图👧
二、什么是协程?
协程:我个人理解的协程就是当遇到需要IO操作时,CPU去执行其他程序,等IO完成后再恢复CPU的使用。使用协程必须使用 loop = asyncio.get_event_loop() 创建超级循环。
三、Python 异步IO库介绍:asyncio
这里面有几个重要的关键词,一个是async ,用于声明函数是异步函数 ,还有await 就是遇到IO阻塞挂起去执行await里面的函数,tasks列表中也可使用ensure_future 来创建。最后一行是执行爬虫直到tasks全部完成。
四、异步编程的威力
- Nginx作为 Web 服务器:
打败了同步阻塞服务器 Apache, 使用更少的资源支持更多的并发连接,体现更高的效率,能够支持高达 50,000 个并发连接数的响应,使用 epoll and kqueue 作为开发模型 - Redis为什么这么快:
处理网络请求采用单线程+使用多路I/O复用模型,非阻塞IO ,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗; - Node.js的优势:
采用事件驱动、异步编程,为网络服务而设计。其实Javascript的匿名函数和闭包特性非常适合事件驱动、异步编程。Node.js非阻塞模式的IO处理带来在相对低系统资源耗用下的高性能与出众的负载能力,非常适合用作依赖其它IO资源的中间层服务 - Go语言的一个优势:
Go 使用Goroutine 和 channel为生成协程和使用信道提供了轻量级的语法,使得编写高并发的服务端软件变得相当容易,很多情况下完全不需要考虑锁机制以及由此带来的各种问题,相比Python单个 Go 应用也能有效的利用多个 CPU 核,并行执行的性能好
五、异步编程核心原理
核心原理1:超级循环
在单线程内实现并发,用一个超级循环(其实就是while true)循环,里面每次轮询处理所有的task
核心原理2:IO多路复用
是一种同步IO模型,实现一个线程可以监视多个文件句柄;
一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;
没有文件句柄就绪时会阻塞应用程序,交出cpu
多路是指网络连接,复用指的是同一个线程
3种实现方式,分别是select,poll,epool
-
select 数据结构:bitmap
最大连接数:1024
fd拷贝:每次调用select拷贝
工作效率:轮询O(N)
-
poll 数据结构:数组
最大连接数:无上限
fd拷贝:每次调用poll拷贝
工作效率:轮询O(N)
-
epool 数据结构:红黑树
最大连接数:无上限
fd拷贝:fd首次调用epool_ctl拷贝,每次调用epoll_wait不拷贝
工作效率:回调O(1)
六、异步编程代码示例
因为协程不支持requests,所以我们使用aiohttp代替。 在协程函数中,可以通过await语法来挂起自身的协程,并等待另一个协程完成直到返回结果。要注意的是,await语法只能出现在通过async修饰的函数中,否则会报SyntaxError错误。
import asyncio
import aiohttp
import blog_spider
async def async_craw(url):
print("craw url: ", url)
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
result = await resp.text()
print(f"craw url: {url}, {len(result)}")
loop = asyncio.get_event_loop()
tasks = [
loop.create_task(async_craw(url))
for url in blog_spider.urls]
import time
start = time.time()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print("use time seconds: ", end - start)
七、信号量机制的应用
我们主要使用信号量机制来控制协程数量,下面三个加#的行是和上一处代码的区别,可以对照学习。
import asyncio
import aiohttp
import blog_spider
semaphore = asyncio.Semaphore(10)
async def async_craw(url):
async with semaphore:
print("craw url: ", url)
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
result = await resp.text()
await asyncio.sleep(5)
print(f"craw url: {url}, {len(result)}")
loop = asyncio.get_event_loop()
tasks = [
loop.create_task(async_craw(url))
for url in blog_spider.urls]
import time
start = time.time()
loop.run_until_complete(asyncio.wait(tasks))
end = time.time()
print("use time seconds: ", end - start)
下一篇我们将分析Gevent 和asyncio 的区别,并讲解如何使用Gevent改造一个异步服务器。
Python进阶之并发编程篇持续更新,欢迎点赞收藏 +关注 上一篇:菜菜的并发编程笔记 |(八)使用多进程multiprocessing进行项目开发 下一篇:菜菜的并发编程笔记 |(十)异步编程库Asyncio和Gevent的对比、使用Gevent改造异步服务器 本人水平有限,文章中不足之处欢迎下方👇评论区批评指正~
如果感觉对你有帮助,点个赞👍 支持一下吧 ~
不定期分享 有趣、有料、有营养内容,欢迎 订阅关注 🤝 我的博客 ,期待在这与你相遇 ~
|