当前位置:首页 > 科技  > 软件

关于FastAPI中在新线程里调用协成函数问题

来源: 责编: 时间:2024-01-04 09:32:39 396观看
导读先前有公众号朋友问起一个问题,大概的问题是这样:在异步接口里面接收批量上传的文件夹后通过webdav3进行批量进行文件处理。其实涉及的问题就是:相对于在异步之中进行线程化的异步处理。大致的代码如下所示:@uploadrp.pos

先前有公众号朋友问起一个问题,大概的问题是这样:xdc28资讯网——每日最新资讯28at.com

在异步接口里面接收批量上传的文件夹后通过webdav3进行批量进行文件处理。其实涉及的问题就是:相对于在异步之中进行线程化的异步处理。xdc28资讯网——每日最新资讯28at.com

xdc28资讯网——每日最新资讯28at.com

大致的代码如下所示:xdc28资讯网——每日最新资讯28at.com

@uploadrp.post('/upload/rp')async def upload_rp_file(file: List[UploadFile] = File(...)):   ........    upload_result = await new_upload_file(file_path=file_path, file_name=file_name, old_name=rp_dir_name)       .........

其中对应的new_upload_file也是异步函数,但是里面涉及到webdav3库的异步的调用,如下代码所示:xdc28资讯网——每日最新资讯28at.com

async def new_upload_file(file_path, file_name, old_name):    """ 上传文件夹 """.........  upload_rp_file_result = client.upload_async('' + file_name, file_path,                                                callback=partial(completion_callback, old_file_dir=old_name, file_name=file_name))  .........

容纳后在调用upload_async的使用,需要进行任务处理完成的回调,也就是completion_callback函数,它的代码如下:xdc28资讯网——每日最新资讯28at.com

def completion_callback(old_file_dir, file_name):    pss

它的问题就是在completion_callback还有一个异步的函数需要调用的时候就遇到问题了:xdc28资讯网——每日最新资讯28at.com

(1) 我无法在upload_async上传文件完成后的回调函数使用async,即 async def completion_callbackxdc28资讯网——每日最新资讯28at.com

(2) 我在completion_callback中获取当前事件循环,因为执行这个回调函数的时候,控制台打印当前没有事件循环xdc28资讯网——每日最新资讯28at.com

3) 我在completion_callback中new一个事件循环(new_event_loop)然后用create_task或run_until_complete执行update_rp_file_status函数的时候,都不成功:xdc28资讯网——每日最新资讯28at.com

  • 要不然就报update_rp_file_status函数跟new的事件循环不是同一个...
  • 要不就是这个update_rp_file_status压根不执行...
  • 要不就是还没执行,new出来的事件循环,在update_rp_file_status还没执行的时候就被销毁了,很多问题...

问题探究

其实这个问题本质其实就是关于 client.upload_async中启用了新的线程去异步处理了,导致了开启了新的线程的问题,如下 client.upload_async的代码:xdc28资讯网——每日最新资讯28at.com

xdc28资讯网——每日最新资讯28at.com

由此可见,问题肯本原因在于是因为我们是在新线程中调用了异步函数,而新线程没有自己的事件循环导致的。xdc28资讯网——每日最新资讯28at.com

回到事件循环的本身上,我们知道首先需要知道一点:xdc28资讯网——每日最新资讯28at.com

异步函数需要在事件循环中运行。但是由于我们启动服务之后,在主线程中,FastAPI应用程序已经创建了一个事件循环,并且通过uvicorn.run()方法来运行该应用程序,而这个的事件循环对象是当前主线程创建出来的, 而当我们在新线程中,也就是client.upload_async调用之后,开启的线程中并没有默认的事件循环可用。因此,当你尝试在新线程中调用异步函数时,就会抛出RuntimeError: There is no current event loop in thread异常,甚至你尝试打印获取当前运行的事件循环都无法获取到。如下代码所示:xdc28资讯网——每日最新资讯28at.com

from fastapi import FastAPIimport asyncioapp = FastAPI()from threading import Thread  # 创建线程的模块def task(name):    print(asyncio.get_event_loop())async def do_task():    # 异步任务的逻辑    await asyncio.sleep(1)    print("异步任务完成")@app.get("/items")async def read_item():    p = Thread(target=task, args=('线程1',))    p.start()      return {"data": "ok"}

在上面的等待吗中我们直接输出当前 print(asyncio.get_event_loop())也会遇到如下类似的错误提示:xdc28资讯网——每日最新资讯28at.com

xdc28资讯网——每日最新资讯28at.com

问题解决

解决此类的问题,开始的时候,因为具体可能没了解带业务细节和逻辑,所以给的思路也有点偏差,后来自己尝试后,其实才发现原来只是一个比较简单的问题,此类的问题,我们可以在新线程中创建一个新的事件循环,并将任务添加到该事件循环中,以确保异步函数能够正确运行。这样,每个线程都有自己的事件循环,可以独立地运行异步任务。也就是如下的代码:xdc28资讯网——每日最新资讯28at.com

from fastapi import FastAPIimport asyncioapp = FastAPI()from threading import Thread  # 创建线程的模块def task(name):    print(asyncio.get_event_loop())    # 尝试去执行异步任务    asyncio.run(do_task())async def do_task():    # 异步任务的逻辑    await asyncio.sleep(1)    print("异步任务完成")@app.get("/items")async def read_item():    p = Thread(target=task, args=('线程1',))    p.start()      return {"data": "ok"}

在上面的代码中使用  asyncio.run开启新的事件循环去处理异步函数即可。但是之前那个朋友也说它尝试够使用在completion_callback中new一个事件循环(new_event_loop)然后用create_task或run_until_complete执行update_rp_file_status函数的时候,都不成功,或许可能得原因是它创建新的事件循环之后,没有进行重新设置,所以会导致无法设置成功,也就是我们也可以改为如下代码进行运行:xdc28资讯网——每日最新资讯28at.com

def task(name):    loop = asyncio.new_event_loop()  # 创建新的事件循环    asyncio.set_event_loop(loop)  # 设置新的事件循环为当前线程的事件循环    loop.run_until_complete(do_task())  # 在新的事件循环中运行异步任务

关于asyncio.run 和手动的设置创建事件循环

asyncio.run()是从Python 3.7版本引入的一个方便的函数,它用于运行异步函数。它自身会自动创建一个新的事件循环,并将异步函数添加到该事件循环中运行。在异步函数完成后,它会关闭事件循环并返回结果。而我们的xdc28资讯网——每日最新资讯28at.com

 loop = asyncio.new_event_loop()  # 创建新的事件循环    asyncio.set_event_loop(loop)  # 设置新的事件循环为当前线程的事件循环    loop.run_until_complete(do_task())  # 在新的事件循环中运行异步

是一种手动创建、设置和运行事件循环的方法包括:xdc28资讯网——每日最新资讯28at.com

使用asyncio.new_event_loop()创建一个新的事件循环。使用asyncio.set_event_loop(loop)将新创建的事件循环设置为当前线程的事件循环。使用loop.run_until_complete(do_task())在新的事件循环中运行异步任务,直到任务完成。xdc28资讯网——每日最新资讯28at.com

他们之间的区别在于使用asyncio.run()时,它会自动处理事件循环的创建、设置和关闭,非常方便。而手动创建、设置和运行事件循环需要更多的代码来处理这些步骤,但也提供了更多的灵活性和控制权。xdc28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-57374-0.html关于FastAPI中在新线程里调用协成函数问题

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: .NET下优秀的IOC容器框架Autofac的使用方法,实例解析

下一篇: Vue3这个API慎用!可能会有性能问题!

标签:
  • 热门焦点
  • Mate60手机壳曝光 致敬自己的经典设计

    8月3日消息,今天下午博主数码闲聊站带来了华为Mate60的第三方手机壳图,可以让我们在真机发布之前看看这款华为全新旗舰的大致轮廓。从曝光的图片看,Mate 60背后摄像头面积依然
  • 6月安卓手机性能榜:vivo/iQOO霸占旗舰排行榜前三

    2023年上半年已经正式过去了,我们也迎来了安兔兔V10版本,在新的骁龙8Gen3和天玑9300发布之前,性能榜的榜单大体会以骁龙8Gen2和天玑9200+为主,至于那颗3.36GHz的骁龙8Gen2领先
  • JavaScript 混淆及反混淆代码工具

    介绍在我们开始学习反混淆之前,我们首先要了解一下代码混淆。如果不了解代码是如何混淆的,我们可能无法成功对代码进行反混淆,尤其是使用自定义混淆器对其进行混淆时。什么是混
  • K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • 2023年,我眼中的字节跳动

    此时此刻(2023年7月),字节跳动从未上市,也从未公布过任何官方的上市计划;但是这并不妨碍它成为中国最受关注的互联网公司之一。从2016-17年的抖音强势崛起,到2018年的“头腾
  • 郭明錤称华为和江淮汽车合作开发问界MPV,定价100万左右、计划明年量产

    8 月 1 日消息,郭明錤今天在 Medium 平台发布博文,称华为正在和江淮汽车合作,开发售价在 100 万元的问界 MPV,预计在 2024 年第 2 季度量产,销量目标为
  • OPPO K11搭载长寿版100W超级闪充:26分钟充满100%

    据此前官方宣布,OPPO将于7月25日也就是今天下午14:30举办新品发布会,届时全新的OPPO K11将正式与大家见面,将主打旗舰影像,和同档位竞品相比,其最大的卖
  • DRAM存储器10月价格下跌,NAND闪存本月价格与上月持平

    10月30日,据韩国媒体消息,自今年年初以来一直在上涨的 DRAM 存储器的交易价格仅在本月就下跌了近 10%,此次是全年首次降价,而NAND 闪存本月价格与上月持平。市
  • 苹果MacBook Pro 2021测试:仍不支持平滑滚动

    据10月30日9to5 Mac 消息报道,苹果新的 14 英寸和 16 英寸 MacBook Pro 2021 上市后获得了不错的评价,亮点包括行业领先的性能,令人印象深刻的电池续航,精美丰
Top