热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python百万并发_Python进阶:百万「并发」基础之异步编程(下篇)

Python进阶:百万「并发」基础之异步编程(下篇)HackPython致力于有趣有价值的编程教学简介在上一节中,了解了yield、yieldfrom等

Python进阶:百万「并发」基础之异步编程(下篇)HackPython致力于有趣有价值的编程教学

简介

在上一节中,了解了yield、yield from等概念,此时Python以及具有编写协程实现「回调」的能力,而「回调」是异步编程的基础,随后Python语言的开发者利用yield from能力,在Python3.4中引入了异步I/O框架asyncio,该框架在Python3.5中被完善并作为标准库之一,用于基于协程的异步I/O编程,本节就来讨论一下asyncio以及async/await等内容。

关键概念

在Python3.4中引入了asyncio.coroutine装饰器来标志函数作为协程函数,协程函数具有协程的特性并与asyncio的事件循环一同使用,实现异步编程的目的,为了避免生成器与协程之间的混淆,在Python3.5中引入了async/await,其中async替代asyncio.coroutine装饰器,await替代yield from,从而让协程的实现更加直观,async/await和yield frome这两种不同风格的协程在底层其实是相互复用相互兼容的,在Python3.6中asyncio库“转正”,成为正式的标注库。

这里只讨论新的写法,即async/await实现协程的方式,Python中协程主要的特性如下:

1.函数使用了async表达式开头,即使它不包含await表达式,也是一个协程函数 2.async协程函数中使用yield或者yield from会阐释SyntaxError错误,即新旧语法不可混合使用 3.与常规生成器类似,协程函数在调用时会返回一个coroutine对象 4.与yield方式实现的协程不同,yield在最后会抛出Stoplteration异常,而async中则是RuntimeError 5.当async创建的协程函数被垃圾回收时,一个未被await的协程会抛出RuntimeWarning异常。

在使用asyncio框架前,需要先了解其中几个概念。

Coroutine 协程

以async表达式开头的函数成为协程函数或简称为协程,如下:import asyncio

async def main():

print('hello')

await asyncio.sleep(1)

print('world')

asyncio.run(main())

上述代码中,main() 为协程函数,其写法是Python3.7的写法,Python3.7中对asyncio的使用做了简化, 如果你使用python3.6为主,其asyncio写法如下:import asyncio

async def main():

print('hello')

await asyncio.sleep(1)

print('world')

def run():

# 创建协程对象

coroutine = main()

# 创建事件循环

loop = asyncio.get_event_loop()

# 将协程对象添加到事件循环中,运行直到结束

loop.run_until_complete(coroutine)

# 关闭事件循环

loop.close()

run()

可以看出,Python3.6中asyncio的用法会复杂一下,在Python3.7中,run()方法已经为我们处理好了创建事件、添加协程对象到事件循环、关闭事件循环等事情 ,即loop asyncio.get_event_loop()

loop.run_until_complete(main())

loop.close()

# 在Python3.7中被替换为

asyncio.run()

当然,asyncio也支持传统的基于生成器的协程,不再多提。

Awaitables 可等待对象

「可等待对象」通常有3类,分别是:

1.协程 coroutine 2.任务 Task 3.未来对象 Future

一个直观的判断方法就是,如果一个对象能够被用在 await 表达式中,那么就可以称这个对象为 「可等待对象」

简单示例如下:import asyncio

async def myprint():

print('hello hackpython')

async def main():

# 直接调用,创建协程对象,不会执行协程中的内容

myprint()

# 协程对象成为被等待对象后,才会执行其中的内容

await myprint()

asyncio.run(main())

上述代码会输出如下内容:use_asyncio4.py:8: RuntimeWarning: coroutine 'myprint' was never awaited

myprint()

RuntimeWarning: Enable tracemalloc to get the object allocation traceback

hello hackpython

从输入内容可以看出,直接使用协程函数是不会执行其中的逻辑的,而且还会因为没有使用而触发相应的警告, 只有利用await成为可等待对象后,才会被asyncio事件循环去执行。await会将控制权交由可等待对象。

Task 任务

「任务」主要用于「并发」的调度协程。

一个协程可以通过asyncio.create_task()函数封装成一个Task,此时这个协程很快就会被自动调度执行,代码如下:import asyncio

async def fun1():

print('hackpython')

async def main():

# 创建任务

task = asyncio.create_task(fun1())

# 作为被等待对象

await task

asyncio.run(main())

asyncio.createtask() 是 Python 3.7 新增的方法,如果是Python3.6还可以用 asyncio.ensurefuture 和 loop.create_task 。

可以将Task理解为协程对象的进一步封装,其中包含着各种状态,简单使用如下:async def a():

print('a funtion start.')

await asyncio.sleep(2)

print('a function end.')

async def main():

# task = asyncio.ensure_future(a())

task = asyncio.create_task(a())

print(task)

print(task.done())

await task

print(task)

print(task.done())

if __name__ == '__main__':

asyncio.run(main())

上述代码输出如下:>

False

a funtion start.

a function end.

result=None>

True

从输出内容可以看出,一开始Task任务的状态为pending(等待状态),调用其done()方法可以判断该任务是否执行 ,可以看出,没有利用await将其转为可等待对象前,Task任务是没有执行的,使用await后,即将控制权交由task对象,此时再次打印,发现其状态改变为finshed(完成状态),调用done()方法后可得该任务被正常执行了 。

Future 未来对象

Future代表着一个未来对象,当异步操作结束后会将最终的结果设置到Future对象上,Future同样是对协程的封装,它是一个偏底层的类,具有比较多的方法可以做一些复杂的操作,但在日常开发时,并不会去使用 ,更多的是使用其Task任务,它其实是Future的一个子类 。

Eventloop事件循环

使用 asyncio 框架时,其实就是开启一个事件循环,事件循环对应的实例提供了注册、取消、执行与回调等方法,方便控制整个事件循环实例。

所谓事件循环,就是将协程函数、任务 Task、未来对象 Future 等注册到事件循环中,事件循环实例会循环执行这些函数 ,注意同一时刻下只执行某个函数对象,具体执行某个函数时,如果执行到函数中进行 I/O 耗时操作的部分,事件循环就会将该函数暂停,而去执行其他函数,等进行 I/O 耗时操作的函数执行完后,会再次加入循环队列,等事件循环下次循环到它时继续从此前位置执行,从而实现这些可异步操作对象的协同运行,达到并发的效果 。

结尾

asyncio是Python中比较复杂但又非常重要的概念,在 百万「并发」基础之异步编程 上、中、下三篇文章中比较系统的讨论了异步编程的概念以及在Python中的实现方式 ,但对生成器、yield from以及asyncio等依旧没有深入探讨 ,在HackPython后面的文章中,会系统性讨论这些概念,最后欢迎学习 HackPython 的教学课程并感觉您的阅读与支持。



推荐阅读
  • 如何高效启动大数据应用之旅?
    在前一篇文章中,我探讨了大数据的定义及其与数据挖掘的区别。本文将重点介绍如何高效启动大数据应用项目,涵盖关键步骤和最佳实践,帮助读者快速踏上大数据之旅。 ... [详细]
  • 本文介绍了如何在Python中使用插值方法将不同分辨率的数据统一到相同的分辨率。 ... [详细]
  • 浅析python实现布隆过滤器及Redis中的缓存穿透原理_python
    本文带你了解了位图的实现,布隆过滤器的原理及Python中的使用,以及布隆过滤器如何应对Redis中的缓存穿透,相信你对布隆过滤 ... [详细]
  • 尽管我们尽最大努力,任何软件开发过程中都难免会出现缺陷。为了更有效地提升对支持部门的协助与支撑,本文探讨了多种策略和最佳实践,旨在通过改进沟通、增强培训和支持流程来减少这些缺陷的影响,并提高整体服务质量和客户满意度。 ... [详细]
  • 投融资周报 | Circle 达成 4 亿美元融资协议,唯一艺术平台 A 轮融资超千万美元 ... [详细]
  • 本文全面解析了 gRPC 的基础知识与高级应用,从 helloworld.proto 文件入手,详细阐述了如何定义服务接口。例如,`Greeter` 服务中的 `SayHello` 方法,该方法在客户端和服务器端的消息交互中起到了关键作用。通过实例代码,读者可以深入了解 gRPC 的工作原理及其在实际项目中的应用。 ... [详细]
  • 本文节选自《NLTK基础教程——用NLTK和Python库构建机器学习应用》一书的第1章第1.2节,作者Nitin Hardeniya。本文将带领读者快速了解Python的基础知识,为后续的机器学习应用打下坚实的基础。 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • 本文介绍如何使用OpenCV和线性支持向量机(SVM)模型来开发一个简单的人脸识别系统,特别关注在只有一个用户数据集时的处理方法。 ... [详细]
  • Python 数据可视化实战指南
    本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
  • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
  • 本文介绍了如何使用Python的Paramiko库批量更新多台服务器的登录密码。通过示例代码展示了具体实现方法,确保了操作的高效性和安全性。Paramiko库提供了强大的SSH2协议支持,使得远程服务器管理变得更加便捷。此外,文章还详细说明了代码的各个部分,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 本文是Java并发编程系列的开篇之作,将详细解析Java 1.5及以上版本中提供的并发工具。文章假设读者已经具备同步和易失性关键字的基本知识,重点介绍信号量机制的内部工作原理及其在实际开发中的应用。 ... [详细]
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 本章节在上一章的基础上,深入探讨了如何通过引入机器人实现自动聊天、表情包回应以及Adidas官方账号的自动抽签功能。具体介绍了使用wxpy库进行微信机器人的开发,优化了智能回复系统的性能和用户体验。通过详细的代码示例和实践操作,展示了如何实现这些高级功能,进一步提升了机器人的智能化水平。 ... [详细]
author-avatar
初学
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有