python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python3 asgiref.sync

Python3跨同步和异步的利器asgiref.sync

作者:andylin02

本文主要介绍了Python3跨同步和异步的利器asgiref.sync,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、asgiref.sync是什么?

asgiref.sync 是 ASGI(Asynchronous Server Gateway Interface)参考实现库 asgiref 中的核心子模块,主要用于安全地桥接同步代码与异步代码。

📌 一句话总结:
它让你在异步环境中调用同步函数(如 Django ORM),或在同步环境中调用异步函数(如管理命令中调用 async API),而不会破坏事件循环或导致线程混乱。

该模块随 Django 3.0+(2019 年底) 成为主流,并成为现代 Python 异步生态的关键粘合剂。

二、核心组件与 API

asgiref.sync 主要提供两个装饰器/转换器类:

组件类型作用
sync_to_async装饰器 / 可调用对象将同步函数 → 异步函数
async_to_sync装饰器 / 可调用对象将异步函数 → 同步函数

此外,还包含内部工具类(通常用户无需直接使用):

三、详细使用方式与场景

1.sync_to_async—— 同步 → 异步

典型场景

基本用法(装饰器)

from asgiref.sync import sync_to_async

@sync_to_async
def get_user_count():
    # Django ORM 是同步的!
    return User.objects.count()

async def my_view(request):
    count = await get_user_count()  # 安全调用
    return JsonResponse({"count": count})

高级用法(带参数)

# thread_sensitive=True(默认):确保同一请求的多次调用在同一线程
@sync_to_async(thread_sensitive=True)
def database_operation(user_id):
    return Profile.objects.get(user_id=user_id).data

# thread_sensitive=False:用于无状态计算(如加密、图像处理)
@sync_to_async(thread_sensitive=False)
def cpu_bound_task(data):
    return heavy_computation(data)

参数说明

参数默认值说明
thread_sensitiveTrue关键! 若为 True,所有调用共享同一线程局部状态(对 Django ORM 至关重要)
executorNone自定义线程池(默认使用专用线程池)

💡 为什么 thread_sensitive=True 如此重要?
Django 的数据库连接、中间件状态等都存储在 线程局部变量(thread-local) 中。
如果两次 ORM 调用在不同线程,会导致连接混乱、事务错乱、甚至数据污染!

2.async_to_sync—— 异步 → 同步

典型场景

基本用法

from asgiref.sync import async_to_sync
import httpx

async def fetch_data(url):
    async with httpx.AsyncClient() as client:
        resp = await client.get(url)
        return resp.json()

# 转换为同步函数
fetch_sync = async_to_sync(fetch_data)

def handle(self, *args, **options):
    data = fetch_sync("https://api.example.com")  # 阻塞等待,但内部使用事件循环
    print(data)

限制

四、底层机制简析

sync_to_async如何工作?

  1. 将同步函数提交到专用线程池执行
  2. 如果 thread_sensitive=True:
    • 使用单线程执行器(而非线程池)
    • 同一“上下文”(如同一请求)的所有调用复用同一线程
  3. 返回一个协程,await 时等待线程结果

async_to_sync如何工作?

  1. 创建一个新的事件循环(如果不存在)
  2. 在该 loop 中运行异步函数
  3. 阻塞当前线程直到完成

这比直接用 asyncio.run() 更智能,尤其对 thread_sensitive 场景做了深度优化。

五、常见问题与陷阱

问题 1:在已有事件循环中使用async_to_sync

async def view():
    async_to_sync(some_async_func)()  # ❌ RuntimeError

✅ 修复:直接 await some_async_func()

问题 2:误设thread_sensitive=False导致 Django ORM 错误

@sync_to_async(thread_sensitive=False)  # ❌ 危险!
def get_user():
    return User.objects.get(id=1)  # 可能报 "connection already closed"

✅ 修复:保持默认 thread_sensitive=True(Django 官方要求)

问题 3:嵌套转换导致性能下降

# 不要这样:
async_to_sync(sync_to_async(func))

✅ 修复:明确边界,避免来回转换

问题 4:忘记 await(sync_to_async 返回的是协程)

async def bad():
    result = sync_to_async(blocking_func)()  # 返回协程对象,未执行!
    print(result)  # <coroutine object...>

✅ 修复:result = await sync_to_async(blocking_func)()

六、与原生 asyncio 方案对比

方案适用场景线程安全Django ORM 兼容推荐度
asgiref.sync.sync_to_async通用同步→异步✅(可选 thread_sensitive)✅✅✅(官方方案)⭐⭐⭐⭐⭐
loop.run_in_executor()简单阻塞任务❌(需手动管理线程)❌(ORM 可能出错)⭐⭐
asyncio.run()顶层启动异步❌(不能嵌套)不适用⭐⭐⭐

关键优势:
asgiref 是唯一能保证 Django ORM 在异步中正确工作的方案。

七、最佳实践

1. Django 用户必读

2. 合理使用thread_sensitive

3. 避免在热路径频繁转换

4. 测试时注意

八、完整示例:Django 异步视图 + ORM

# views.py
from django.http import JsonResponse
from asgiref.sync import sync_to_async
from .models import Article

@sync_to_async
def get_article_count():
    return Article.objects.count()  # 同步 ORM

@sync_to_async
def create_article(title):
    return Article.objects.create(title=title)

async def article_stats(request):
    count = await get_article_count()
    return JsonResponse({"total": count})

async def create_article_view(request):
    title = request.GET.get("title")
    article = await create_article(title)
    return JsonResponse({"id": article.id, "title": article.title})

此代码在 Django 3.1+ 中完全合法且安全。

九、总结

项目说明
核心价值安全桥接同步与异步世界,尤其保障 Django ORM 在异步中的正确性
两大工具sync_to_async(同步→异步)、async_to_sync(异步→同步)
关键特性thread_sensitive 保证线程局部状态一致性
主要场景- Django 异步视图调用 ORM
- 同步脚本调用异步 API
- 任何混合异步/同步的项目
安装方式通常随 Django 自动安装;单独安装:pip install asgiref
版本要求Python 3.6+,asgiref ≥ 3.2(推荐 ≥ 3.5)

终极口诀:
“异步调同步,用 sync_to_async;
同步调异步,用 async_to_sync;
Django ORM 必加 thread_sensitive=True!”

通过 asgiref.sync,你可以在享受异步高性能的同时,无缝使用庞大的同步生态(Django、SQLAlchemy、requests 等),是现代 Python 异步开发的必备桥梁工具。

到此这篇关于Python3跨同步和异步的利器asgiref.sync的文章就介绍到这了,更多相关Python3 asgiref.sync内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文