首页 > 编程语言 >协程&异步编程

协程&异步编程

时间:2024-03-28 18:44:06浏览次数:31  
标签:__ 异步 协程 编程 print async def asyncio

协程,也可以被称为微线程,是一种用户态内的上下文切换技术。简而言之,其实就是通过一个线程实现代码块相互切换执行

协程一般应用在有IO操作的程序中,因为协程可以利用IO等待的时间去执行一些其他的代码,从而提升代码执行效率。

async

事件循环

事件循环,可以理解为while循环,在周期性的执行一些任务,在任务列表为空时终止循环。

# 伪代码

任务列表 = [ 任务1, 任务2, 任务3,... ]

while True:
    可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行'和'已完成'的任务返回
    
    for 就绪任务 in 已准备就绪的任务列表:
        执行已就绪的任务
        
    for 已完成的任务 in 已完成的任务列表:
        在任务列表中移除 已完成的任务

	如果 任务列表 中的任务都已完成,则终止循环

协程函数,定义形式为 async def 的函数。

协程对象,调用 协程函数 所返回的对象。

执行协程函数,需要 事件循环协程对象 配合实现。

import asyncio


# 定义一个协程函数
async def func():
    print('我是tasks')
    
# 调用协程函数,返回一个协程对象  注意:函数内部代码不会执行
result = func()

#方式1
# loop = asyncio.get_event_loop()   # 创建一个事件循环
# loop.run_until_complete(result)   # 将协程当做任务提交到事件循环的任务列表中,协程执行完成之后终止。

# 方式2
asyncio.run(result)   # asyncio.run 函数在 Python 3.7 中加入 asyncio 模块

awit

await是一个只能在协程函数中使用的关键字,用于遇到IO操作时挂起 当前协程(任务),当前协程(任务)挂起过程中 事件循环可以去执行其他的协程(任务),当前协程IO处理完成时,可以再次切换回来执行await之后的代码

示例

import asyncio

# await后面可以加 协程对象 task对象 future对象
async def download():
    print('start')
    await asyncio.sleep(2)
    print('end')
    return 'ok'

async def func():
    print('我是tasks')
    res = await download()
    print('结束',res)
    res2 = await download()
    print('结束', res2)

asyncio.run(func())

上述的示例只创建了一个任务,所以无法切换到其他任务

在程序想要创建多个任务对象,需要使用Task对象来实现。

Task对象

Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,可以让协程加入事件循环中等待被调度执行。

task 添加任务放到事件循环

示例1

import asyncio

async def download():
    print('start')
    await asyncio.sleep(2)
    print('end')
    return 'ok'

async def func():
    print('main')
    # 创建协程,将协程封装到一个Task对象中并立即添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。
    task1 = asyncio.create_task(download())
    task2 = asyncio.create_task(download())
    print('end')

    ret1 = await task1
    ret2 = await task2
    print(ret1,ret2)

asyncio.run(func())

示例2

import asyncio

async def download():
    print('start')
    await asyncio.sleep(2)
    print('end')
    return 'ok'
    
# 常规写法
async def func2():
    print('main')
    task_list = [
        asyncio.create_task(download(),name='t1'),
        asyncio.create_task(download(),name='t2'),
    ]
    print('end')
    done,_ = await asyncio.wait(task_list)
    print(done)

asyncio.run(func2())

注意:asyncio.wait 源码内部会对列表中的每个协程执行ensure_future从而封装为Task对象,所以在和wait配合使用时task_list的值为[func(),func()] 也是可以的。

示例3

import asyncio

async def download():
    print('start')
    await asyncio.sleep(2)
    print('end')
    return 'ok'

# 放到外部执行
def main():
    task_list = [
            download(),
            download()
        ]
    # 错误:task_list = [ asyncio.create_task(download()), asyncio.create_task(download()) ]  
    # 此处不能直接 asyncio.create_task,因为将Task立即加入到事件循环的任务列表,
    # 但此时事件循环还未创建,所以会报错。
    
    done,_ = asyncio.run(asyncio.wait(task_list))
    print(done)
    # 使用asyncio.wait将列表封装为一个协程,并调用asyncio.run实现执行两个协程
    # asyncio.wait内部会对列表中的每个协程执行ensure_future,封装为Task对象。

main()

asyncio.Future对象

task 继承 funture ,task对象内部await结果的处理基于funture对象来的

import asyncio

async def main():
    # 获取当前事件循环
    loop = asyncio.get_running_loop()
    # # 创建一个任务(Future对象),这个任务什么都不干。
    fut = loop.create_future()
    # 等待任务最终结果(Future对象),没有结果则会一直等下去。
    await fut

asyncio.run(main())

futures.Future对象

在Python的concurrent.futures模块中也有一个Future对象,这个对象是基于线程池和进程池实现异步操作时使用的对象。

import asyncio
import time
from concurrent.futures.thread import ThreadPoolExecutor

def func():
    time.sleep(1)
    return 123

def main():
    pool = ThreadPoolExecutor(max_workers=5)
    fut = pool.submit(func)
    print(fut)

main()

两个Future对象是不同的,他们是为不同的应用场景而设计,例如:concurrent.futures.Future不支持await语法 等。

在Python提供了一个将futures.Future 对象包装成asyncio.Future对象的函数

两个Future混合使用

import asyncio
import time
from concurrent.futures.thread import ThreadPoolExecutor

def func():
    time.sleep(1)
    return 123

# 将线程池的future对象转换为async的future对象
async def main2():
    loop = asyncio.get_running_loop()
    #获取当前的事件循环

    #1.为 None 默认生成 ThreadPoolExecutor,任务加入线程池里运行,返回future
    fut = loop.run_in_executor(None,func)
    result = await fut
    print(result)
    # 2.
    # with ThreadPoolExecutor() as pool:
    #     fut = await loop.run_in_executor(pool,func)
    #     print(fut)

asyncio.run(main2())

应用场景

# 异步与非异步模块的案例
import asyncio
import requests

async def download(url):
    print('开始下载',url)
    loop = asyncio.get_event_loop()
    #requests模块不支持异步操作,所以通过线程池配合
    fut = loop.run_in_executor(None,requests.get,url)
    response = await fut
    print('下载完成')
    file_name = url.rsplit('/')[-1]+'.jpg'
    with open(file_name,'wb') as f:
        f.write(response.content)
def main3():
    url_list = [
        "https://pic.quanjing.com/jp/1w/QJ6144539261.jpg@!350h",
        "https://pic.quanjing.com/lv/8f/QJ6625036082.jpg@!350h",
        "https://pic.quanjing.com/12/90/QJ8124912494.jpg@!350h"
    ]
    tasks = [download(url) for url in url_list]
    asyncio.run(asyncio.wait(tasks))

main3()
# 注:此案例耗费了线程池在等待任务结束,推荐使用aiohttp

异步迭代器

实现了 __aiter__()__anext__() 方法的对象,__anext__ 必须返回一个 awaitable 对象

async for 会处理异步迭代器的 __anext__() 方法所返回的可等待对象,直到其引发一个 StopAsyncIteration 异常

示例

import asyncio

class AsyncIterator():
    def __init__(self):
        self.counter = 0

    def __aiter__(self):
        return self

    async def __anext__(self):
        if self.counter >= 10:
            raise StopAsyncIteration
        self.counter += 1
        return self.counter
    
async def func():
    iter = AsyncIterator()
    async for i in iter:
        print(i)

asyncio.run(func())

异步上下文管理器

定义 __aenter__()__aexit__() 方法来对 async with 语句中的环境进行控制

示例

import asyncio

class AsyncContextManager:
    def __init__(self,conn):
        self.conn = conn

    async def __aenter__(self):
        # 异步连接数据库
        print('连接')
        self.conn = await asyncio.sleep(2) #修改为数据库连接
        return self

    async def __aexit__(self, exc_type, exc, tb):
        # 异步关闭数据库连接
        print('关闭')
        await asyncio.sleep(2)  # 修改为关闭数据库连接

    async def do_something(self):
        #操作数据库
        await asyncio.sleep(2)
        return 123

async def func2():
    async with AsyncContextManager('conn') as f:
        result = await f.do_something()
        print(result)

asyncio.run(func2())

这个异步的上下文管理器还是比较有用的,平时在开发过程中 打开、处理、关闭 操作时,就可以用这种方式来处理。

uvloop

uvloop是 asyncio 中的事件循环的替代方案,替换后可以使得asyncio性能提高

示例

#pip3 install uvloop

import asyncio
import uvloop

# uvloop是asyncio的事件循环的代替方案. 事件循环>默认asyncio的事件循环
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

# 与之前的一样
#内部的事件循环自动转化为uvloop
asyncio.run(...)

标签:__,异步,协程,编程,print,async,def,asyncio
From: https://www.cnblogs.com/lijun-goods/p/18102373

相关文章

  • 【感悟《剑指offer》典型编程题的极练之路】02字符串篇!
    ​                                                                 个人主页:秋风起,再归来~                                        ......
  • 在Python中如何使用协程进行并发操作
    在Python中使用协程进行并发操作是一种高效的方式来处理I/O密集型任务或者在单个Python程序内部执行多个操作。本文将详细介绍如何在Python中使用协程进行并发操作,包括协程的基本概念、如何创建和运行协程、如何使用任务来管理多个协程,以及如何利用协程进行并发网络请求等。最......
  • 12-Ajax异步交互技术
     同步与异步操作最主要的区别:同步操作必须按照以上步骤执行,而异步操作在第四步响应客户端时,可以继续执行第二步请求服务器,即客户端可以执行其它操作 数据地址:console-mock.apipost.cn/mock/4250f8d4-b605-47eb-9777-07e29548dbb8/list <!DOCTYPEhtml><htmllang="......
  • Ajax 与 Axios 异步请求
    Ajax与Axios异步请求一、服务器对外提供了哪些资源1.网页中如何请求数据 数据,也是服务器对外提供的一种资源。只要是资源,必然要通过请求–处理–响应的方式进行获取。如果要在网页中请求服务器上的数据资源,则需要用到XMLHttpRequest对象。XMLHttpRequest(简称xhr)是......
  • GO并发编程
    Go语言的并发编程是其核心特性之一,它提供了简洁强大的机制来处理并发任务。Go并发模型的基石是goroutines和channels。GoroutinesGoroutine是Go语言中实现并发的基本单位。你可以把它看作一个轻量级的线程,由Go运行时(runtime)进行管理。启动一个新的goroutine非......
  • Python数据库编程全指南SQLite和MySQL实践
    本文分享自华为云社区《Python数据库编程全指南SQLite和MySQL实践》,作者:柠檬味拥抱。1.安装必要的库首先,我们需要安装Python的数据库驱动程序,以便与SQLite和MySQL进行交互。对于SQLite,Python自带了支持;而对于MySQL,我们需要安装额外的库,如mysql-connector-python。#安装MyS......
  • 区块链编程七大语言,使用最多的竟是Java
    SQL——结构化查询语言(StructuredQueryLanguage)或“Sequel”,是IBM开发的一种编程语言,用于与存储、查询和处理数据的数据库进行沟通。如今SQL约拥有700万名开发者。MySQL、PostgreSQL、SQLServer、DB2、Oracle等主流数据库都使用SQL来开发应用程序。使用SQL的区块链项......
  • 抽象的艺术:Go 语言中的编程哲学
    抽象的艺术:Go语言中的编程哲学原创 TimLiu 爱发白日梦的后端 2024-03-2507:00 广东 1人听过 爱发白日梦的后端专注Go语言领域的发展,学习成为更牛逼的架构师,日常分享Go语言、架构、软件工具的使用。179篇原创内容公众号点击上方“名片”,关......
  • 泛型编程之模板
    1.函数模板重要行:template<typenameT,typenameT1>关键值class和typename含义相同,那么我们以后就使用typename即可。 一般情况下的格式:template<模板参数列表>返回值类型函数名(函数参数) 模板参数列表的理解:函数参数列表在运行时调用者用实参来初始化形参而......
  • 23.异步模式-生产者、消费者
    1.与保护性暂停GuardedObject不同,不需要产生结果与消费结果的线程一一对应。2.消费队列可以用来平衡生产和消费的线程资源。3.生产者负责产生结果数据,不关心数据该如何处理,而消费者专心处理结果数据。4.消息队列是有容量限制的,满时不会再加入数据,空时不会再消耗数据。5.jdk......