首页 > 编程语言 >Python协程asyncio

Python协程asyncio

时间:2023-05-11 20:46:34浏览次数:54  
标签:task 协程 Python 对象 Task loop asyncio

  在Python使用multiprocessing进行多线程和多进程操作 这篇文章中介绍了使用多线程的方式对一些I/O操作(文件读写、网络请求,这些操作不用等待其结束,在此期间可以做其他事情)进行加速。而本篇文章介绍的协程可以理解成“微线程”,不开辟其他线程,只在一个线程中执行,并且执行函数时可以被中断的一种异步执行功能。我们可以把采用线程实现的阻塞式I/O操作转化为采用协程实现地异步I/O操作。与多线程比较,没有切换线程的开销和多线程锁机制,因此使用协程可以更加高效实现异步操作。

1、async与await关键字

  Python3.5之后我们可以采用async和await这两个关键字实现协程操作。它的基本原理与生成器(Python迭代器、生成器、装饰器的使用)类似,被asycn关键字修饰的函数,在函数被调用时不会立刻执行函数体内容,而是在等到需要时再一项一项的获取。协程可以从执行环境中获取输入值,并把这个函数执行后的结果放到这个执行环境中。协程与线程的区别在于,它不会把这个函数从头到尾的执行,而是遇到一个await表达式,就暂停一次,下次继续执行的时候,它会等待await所针对的那项操作(这个操作就是用async修饰的函数)有了结果,然后再推进到下一个await表达式那里。

  Python可以让数量极多的async函数各自向前推进,看起来像很多条Python线程那样能够并发地运行。我们先看一个最简单的异步调用的例子:

import asyncio

async def async_task(name):
    print(f"任务{name} 开始执行")
    await asyncio.sleep(1)
    print(f"任务{name} 执行结束")
    return f"返回结果{name}"

result1 = async_task(1)
result2 = async_task(2)
result3 = async_task(3)
print("result的类型: ", type(result1))

  我们这里使用async定义了一个函数叫做async_task,这个函数传入一个参数name,函数体我们使用await asyncio.sleep(1) 模拟I/O堵塞1s的操作(注意这里不能使用time.sleep()函数来模拟,因为time.sleep()会将当前线程休眠并释放GIL,而对于协程来说我们只有一个线程,就是主线程,如果使用time.sleep()就是在堵塞主线程)。我们执行上述代码结果如下。

  我们会发现函数体并没有被执行(函数体内部的print没有打印),而且我们函数的结果也是一个“coroutine”类型,并不是我们return返回值。与此同时系统还抛出了一个运行警告。这是因为上述代码其实只是在定义协程任务(执行async修饰的函数会得到一个coroutine对象),继续运行下面的代码,其实才是真正的开启了协程。

tasks = asyncio.wait([result1, result2, result3])
print("tasks类型: ", type(tasks))
asyncio.run(tasks)

  这里的asyncio.wait()传入了一个coroutine对象的可迭代对象,asyncio.wait()会将这些任务进行打包整个生成一个任务并返回corountine对象(也就是tasks的类型)。最后一行asyncio.run()需要传入一个coroutine对象(可以直接传入result1,但不能直接传入[result1, result2, result3]因为这是一个list,所以我们需要先采用asyncio.wait()将这些任务打包),此时就会开启协程,运行函数体。我们看下运行结果:

   我们可以看到此时才会真正的执行我们定义的函数体的内容(并且在开始执行和执行结束之间是有1s的停顿的,大家可以使用time库自行测试程序运行时间)。我们采用async和asyncio.run()完成了一个最简单的异步协程任务的使用。

2、Task和事件循环

  在上面个例中我们已经可以简单的使用协程来完成异步操作了,但是我们会发现我们无法得到函数的返回结果。为了能得到任务函数的返回结果,我们还需要了解到协程更底层的一些概念和操作。首先我们要介绍两个概念:事件循环(Loop)、Task类。

  • 事件循环(Loop):管理所有的任务,在整个程序运行过程中不断循环执行并追踪这些任务发生的顺序,并将它们放在执行队列中,空闲时调用相应的事件处理者来处理这些任务。可以看作将任务放在Loop中,那么会让CPU去执行这些任务,而Loop则起到一个管家和追踪者的作用。实际上是ProactorEventLoop类。
  • Task类:这个类是asyncio.Task,它是asyncio.Future的子类。Future对象一般表示尚未完成的计算/结果,会在将来得到结果,这个对象一般不会直接创建,而是作为Task的基类。Task其实就是真正的运行对象,它可以看作是对协程函数的进一步封装,而Loop直接管理的也是Task对象。我们在上一节提到的“coroutine”类会在内部自动封装成Task类。

  我们下面来介绍如何获得Loop对象和Task对象。

2.1、事件循环对象

  我们可以通过以下方式获得事件循环对象:

  • asyncio.get_running_loop():获取正在运行的事件循环对象(如果当前线程中没有正在运行的事件循环则会报错)
  • asyncio.get_event_loop():获得一个事件循环对象,创建一个新的事件循环loop
  • asyncio.set_event_loop(loop):为当前线程设置一个事件循环对象(该函数会返回一个事件循环对象)
  • asyncio.new_event_loop():创建一个新的事件循环

   我们得到一个Loop对象后,就可以事项Loop对象的方法来启动协程了(上一节启动协程的方式是使用asyncio.run()这是比较高层次的API,查看其源码可以知道其实内部也是获取了Loop对象,再使用Loop.run_until_complete()启动协程的),因此针对上一节的代码,我们还可以使用以下代码来启动异步协程任务。

loop = asyncio.get_event_loop()
loop.run_until_complete(tasks)

  loop.run_until_complete()这个函数从函数名就可以知道,含义是:启动事件循环,直到里面的Future全部完成(这里看成协程任务全部完成也是可以的)。这个函数是有返回值的,会根据你传入的参数(传入的参数必须是Task对象、Future对象、coroutine对象)而得到不同的返回值。如果你是传入的Task对象,那么你的返回值就是这个Task对象执行后的返回值,例如我们直接执行一个协程函数。

import asyncio

async def async_task(name):
    print(f"任务{name} 开始执行")
    await asyncio.sleep(1)
    print(f"任务{name} 执行结束")
    return f"返回结果{name}"

loop = asyncio.get_event_loop()
result = loop.run_until_complete(async_task(1))
print(result)

  最终的执行结果如下:

   可以看到我们成功得到了协程函数的返回值(这也就解决了本节开头提出的问题:如何获取协程函数的返回值)。后面我们还会介绍如果loop.run_until_complete()传入的参数是其他对象那么所得到的返回值类型是不同的。

2.2、Task对象

  我们常通过以下方式来得到一个Task对象:

  • loop.create_task(corootine):从一个“coroutine”对象(被async修饰函数执行后就是一个coroutine)得到一个Task对象
  • asyncio.ensure_future(coroutine):从一个“coroutine”对象得到一个Task对象(推荐使用)

   可能有疑问为什么需要使用这些方法去得到Task对象呢,反正asyncio内部在执行任务时不就会将coroutine封装成Task执行么?其实是因为Task是最基本的可操作的协程对象(其实Future才是最基本的,不过我们一般都涉及不到这么底层,所以后文提到Future其实是指的其常用子类比如Task类)我们后续的一些操作比如给任务添加回调函数(callback),这都需要基于Task对象。并且我们还可以查看Task对象的当前状态(是否被执行了),以及执行后查看Task对象的返回值。下面我们通过手动将coroutine封装成Task对象,并使用Loop执行任务,看看会发生什么。

import asyncio

async def async_task(name):
    print(f"任务{name} 开始执行")
    await asyncio.sleep(1)
    print(f"任务{name} 执行结束")
    return f"返回结果{name}"

loop = asyncio.get_event_loop()
task = loop.create_task(async_task(1))
print("类型:", type(task))
print('任务运行前状态: ', task)
loop.run_until_complete(task)  # 运行task对象
print('\n任务运行后状态: ', task)
print("任务返回值: ", task.result())

  我们在代码第十行通过loop.create_task()将一个执行后的协程函数(得到coroutine对象)转化为了一个Task类对象,并且在执行任务前和任务执行后都分别打印了task。需要注意的是我们在代码第13行并没有接收loop.run_until_complete的结果,取而代之的是我们在代码的最后一行直接调用task.result()返回了这个任务的返回值(也就是函数return的那个字符串)。运行后结果如下:

   我们可以看到运行前的task和运行后的task有着明显的不同,运行后的task具有result这个内容并且还有finished标识,而运行前则是pending标识。并且我们最后也成功的使用task.result()得到了任务的返回值。

标签:task,协程,Python,对象,Task,loop,asyncio
From: https://www.cnblogs.com/CircleWang/p/17390920.html

相关文章

  • Python OOP & Class private method All In One
    PythonOOP&ClassprivatemethodAllInOnePythonClassprivatemethoddemos代码缩进错误调用私有方法错误#!/usr/bin/python3#类定义classpeople:#定义基本属性name=''age=0#定义私有属性,私有属性在类外部无法直接进行访问_......
  • Python range function All In One
    PythonrangefunctionAllInOnerange函数函数语法range(stop)range(start,stop[,step])参数说明:start:计数从start开始。默认是从0开始。例如range(5)等价于range(0,5)stop:计数到stop结束,但不包括stop。例如:range(0,5)是[0,1,2,3,4]没有......
  • Python的基础核心知识
    编程语言和编程编程语言语言:人与人之间沟通的媒介编程语言:人与计算机沟通的语言编程程序员通过计算机能够读懂的语言把自己的思想和逻辑写下来的过程编程的初衷是更好的奴隶计算机计算机五大组成部分部1.控制器2.运算器3.存储器4.输出设备5.输入设备计算机三大核心硬......
  • 初学计算机python
    今天正式开始学习计算机python,从最基础的概念开始。学习计算机需要使用一款编辑软件辅助学习,Typora是个很好的选择。初学Typora目前只对一些基本快捷键做了解,做前期准备。对六种标题快捷键,几个#就是几级标题,或者直接Ctrl+1、2、3、4、5、6转变相应等级标题。另外还有无序标题:“......
  • python异步正则字符串替换,asyncio异步正则字符串替换re
     自然语言处理经常使用re正则模块进行字符串替换,但是文本数量特别大的时候,需要跑很久,这就需要使用asyncio异步加速处理importpandasaspdimportreimportasynciodata=pd.read_csv("guba_all_post_20230413.csv")data.dropna(inplace=True)#defreplace_betwee......
  • python异步字符串查找,asyncio和marisa_trie
     自然语言处理当中经常需要字符串的查找操作,比如通过查找返回字串在文本当中的位置,比如通过匹配实现的nerimportpandasaspdimportasyncio#data=pd.read_csv("guba_fc_result_20230413.csv")data=pd.read_csv("guba_all_post_20230413.csv")filename="cate_gr......
  • R语言中调用调用python
       ......
  • python3.11 支持TA-lib
    网上找了很多地方才找到的,以下是原网面内容分享给大家:原网址python3.11支持TA-lib-简书(jianshu.com) python3.11支持TA-libasmcos关注IP属地:北京2022.11.0316:42:52字数122阅读1,075做量化的朋友都知道Ta-lib的windows库相当难编译。python3.11性能提升......
  • python 下载整个网页到本地(保持原有链接)样式
    pipinstallpagesnapplaywrightinstallpython310以上命令:pagesnap网址 网络名称提示:请求成功response.url='https://www.cnpython.com/pypi/singlefile'请求成功response.url='https://www.cnpython.com/media/pypi/static/css/brands.0c9eb08b.css'资源类型为css请求......
  • Linux 安装python、flaks
    linux默认是带有python的下载并安装python3下载地址:https://www.python.org/ftp/python/3.8.2/Python-3.8.2.tgz--创建安装目录mkdir-p/usr/local/python3 --解压安装包tar-zxvfPython-3.8.2.tgz --进入目录编译安装cdPython-3.8.2./configure--pre......