Python异步编程技术概述(Asyncio生态圈+并发爬虫测试)

本文介绍了Python异步编程的基础概念,包括同步/异步、阻塞/非阻塞和协程。探讨了Python的asyncio库如何解决高并发问题,并详细说明了asyncio生态系统,如Web框架、消息队列和数据库驱动。通过对比多线程和异步IO,展示了asyncio的优势。最后,通过一个异步爬虫测试,验证了不同方案的效率,突出了asyncio与aiohttp在处理IO密集型任务时的效能。
摘要由CSDN通过智能技术生成

Python 异步IO技术

异步(asynchronous)编程模式是相对于同步方式的另一种编程思路。 C10K的问题提出后, 各种编程语言都出现了解决高并发的技术栈, 而早在Python2时期, Twisted、Tornado和Gevent这三个库用不同的技术路径解决了高并发。[1] 其中就有用到Python的一些异步实现方法。 此文将对异步编程的基本思想和Python的实现方式进行阐述。

  • 概念解释,同步\异步\阻塞\非阻塞\协程
  • 高并发问题解决方案:多线程与异步IO
  • asyncio解决的问题
  • Python异步IO生态圈
  • 并发哪家强?应用:异步爬虫测试

概念解释,同步\异步\阻塞\非阻塞\协程

同步异步(Synchornous\Asynchronous)

这一概念是针对主程序来说的, 如果主程序遇到阻塞的任务时选择等待,那么这种行为就是同步的, 如果不等待选择执行其他的任务,就是异步的;

阻塞非阻塞(Blocking\Unblocking)

这一概念是针对任务来说的, 如果某项任务在执行费时IO操作时不能挂起跳出(不能把执行权归还主程序)那么此项任务就是阻塞的, 相反就是非阻塞的。

其实不必将上面四个概念区分的很清楚,只需明白异步非阻塞编程的基本思路是将IO密集型的任务时间节省出来让CPU尽可能多地去完成计算密集型任务

协程(coroutines)

Python最早是用生成器写的协程,3.4后可以用asyncio.coroutine装饰一个协程,3.5加入了新的关键字async\await协程成为新的语法。3.6还引入了异步生成器。

协程的定义由很多,我比较认同可以挂起中断的函数 这一说法,前面概念中讲到非阻塞的任务是可以挂起中断的,那么协程正可以实现此项任务;

协程包含两种情况:

  • 协程函数:async def 或者 @asyncio.coroutine
  • 协程函数所返回的对象

协程的运作方式:

  • 通过result = await future或者 result = yeild from future,悬挂协程,直到future完成,获取future的结果/异常
  • 通过 result = await coroutine 或者 result = yeild from coroutine 等待另一个协程的结果(或者异常,异常会被传播)。
  • returen expression 返回该协程的结果,被await,或者yield from获取。
  • raise exception,抛出异常,被await,或者yield from获取。

协程的典型应用案例,摘自《Fluent Python》

# 计算移动平均值

from collections import namedtuple
Result = namedtuple('Result', 'count average')
# 子生成器
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield
        if term is None:
            break
        total += term
        count += 1
        average = total/count
        return Result(count, average)

# 委派生成器
def grouper(results, key):
    while True:
        results[key] = yield from averager()

# 客户端代码,即调用方
def main(data):
    results = {}
    for key, values in data.items():
        group = grouper(results, key) 
        # 协程需要激活
        next(group)
        for value in values:
            # 传递值给自生成器
            group.send(value) 
        group.send(None) # 重要! 

    report(results)

# 输出报告
def report(results):
    for key, result in sorted(results.items()):
    group, unit = key.split(';')
    print('{:2} {:5} averaging {:.2f}{}'.format(
    result.count, group, result.average, unit))

data = {
'girls;kg':
[40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
'girls;m':
[1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
'boys;kg':
[39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
'boys;m':
[1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}
if __name__ == '__main__':
    main(data)

调用协程函数并不能使该协程运行。调用协程函数所返回的协程对象,在被你安排执行之前,不会做任何事情。有两种方式可以启动它:[2]

  • 通过在一个已经启动的协程中调用:await coroutine或者yield from coroutine
  • 或者通过ensure_task()以及loop.create_task()安排协程的执行。(使用asyncio库)

高并发问题解决方案:多线程与异步IO

其实解决高并发就是实现的程序的异步和非阻塞,而多线程\多进程是大多数比较熟悉的方案,将阻塞任务挂起到多个线程, 由操作系统实现线程\进程间的调度concurrent.futures对象的出现使得多线程的非阻塞任务可以使用回调函数, 参考下例:

from concurrent.futures import ThreadPoolExecutor, as_completed, wait, FIRST_COMPLETED
from concurrent.futures import Future
from multiprocessing import Pool
from functools import partial
import time

def get_html(times):
    t
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值