---
title: "异步支持"
version: 3.0
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/3.0/topics/async/
canonical: https://djangodocs.dev/zh-hans/3.0/topics/async/
---
# 异步支持

> **New in Django 3.0**

Django 已对异步 ("async") Python 进行了支持，但还没有支持异步视图或中间件；它们可能在未来的版本中支持。

对异步生态系统的其他部分的支持有限；换句话说，Django 原生的使用 [ASGI](/zh-hans/3.0/howto/deployment/asgi/) 和一些异步安全的支持。

## 异步安全

Django 的一些关键部分无法在异步环境里安全运行，因为它们的全局状态不支持协同工作。这些 Django 部分被归类为 "不安全异步"，并且受到保护，无法在异步环境中执行。ORM 是主要的例子，但其他部分也以这种方式受到保护。

如果你试着从具有 *运行事件循环* 的线程中运行这些部分中的任何一个，你将得到 [`SynchronousOnlyOperation`](/zh-hans/3.0/ref/exceptions/#django.core.exceptions.SynchronousOnlyOperation) 错误。注意，你不必在异步函数内部即可得到这个错误。如果你直接从一个异步函数中调用了同步函数，而没有经过类似 [`sync_to_async()`](#asgiref.sync.sync_to_async) 或线程池之类的操作，那么它也可能会报这个错误，因为你的代码仍然在异步上下文中运行。

如果遇到了这个错误，你应该修改代码以免在异步上下文中调用这个有问题的代码；相反，你可以编写代码在同步函数中与不安全异步交流，使用  [`asgiref.sync.sync_to_async()`](#asgiref.sync.sync_to_async) 或在它自己线程内部运行任何其他首选方式来调用。

如果你 *绝对* 迫切需要从异步上下文中运行此代码 - 比如，它是由外部环境强加给你，并且你确定它不会同时运行（例如在 [Jupyter](https://jupyter.org/) notebook 里），那你可以使用 `DJANGO_ALLOW_ASYNC_UNSAFE` 环境变量来禁用告警。

> **Warning**
>
> 如果启用了这个选项，并且同时访问 Django 的异步不安全 (async-unsafe) 部分，你会遇到数据丢失或损坏，所以一定要非常小心，并且不要再生产环境里这样使用。

如果你需要在 Python 中执行此操作，请使用 `os.environ` ：

```
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
```

## 异步适配函数

当从异步的上下文中调用同步的代码时，有必要适配调用风格，反之亦然。为此，有两个适配器功能，可从 `asgiref.sync` 包中获取：[`async_to_sync()`](#asgiref.sync.async_to_sync) 和 [`sync_to_async()`](#asgiref.sync.sync_to_async) 。它们用于同步和异步之间调用风格的转换，同时保持兼容性。

这些适配函数广泛应用于 Django。[asgiref](https://pypi.org/project/asgiref/) 包本身就是 Django 项目的部分，并且它在当你用 `pip` 方式安装 Django 时，会作为依赖项目自动安装。

### `async_to_sync()`

#### `async_to_sync(async_function, force_new_loop=False)`

包装一个异步函数并且返回一个同步函数。可以用作直接包装器或装饰器：

```
from asgiref.sync import async_to_sync

sync_function = async_to_sync(async_function)

@async_to_sync
async def async_function(...):
    ...
```

如果存在异步函数，那么它会在当前线程的事件循环中运行。如果没有当前事件循环，则会为异步函数专门启动一个新的事件循环，并且会在它完成后再次关闭。无论哪种情况，异步函数会在调用代码的不同线程上执行。

Threadlocals 和 contextvars 值在两个方向的边界上都保持不变。

[`async_to_sync()`](#asgiref.sync.async_to_sync) 本质上是 Python 标准库中 [`asyncio.run()`](https://docs.python.org/3/library/asyncio-runner.html#asyncio.run) 函数更强大的版本。在确保 threadlocals 工作之外，当在它下面使用包装时，它也会启用 [`sync_to_async()`](#asgiref.sync.sync_to_async) 的 `thread_sensitive` 模式。

### `sync_to_async()`

#### `sync_to_async(sync_function, thread_sensitive=False)`

包装一个同步函数并且返回一个异步函数。可以用作直接包装器或装饰器：

```
from asgiref.sync import sync_to_async

async_function = sync_to_async(sync_function)
async_function = sync_to_async(sensitive_sync_function, thread_sensitive=True)

@sync_to_async
def sync_function(...):
    ...
```

Threadlocals 和 contextvars 值在两个方向的边界上都保持不变。

同步函数倾向于假设它们在主线程中运行时编写，因此 [`sync_to_async()`](#asgiref.sync.sync_to_async) 有两个线程模式：

- `thread_sensitive=False` (默认)：同步函数将在一个全新的线程中运行，该线程一旦完成，将会关闭。
- `thread_sensitive=True`: 同步函数将与所有其它  `thread_sensitive`  函数在相同线程里运行，如果主线程是同步的并且你正在使用 [`async_to_sync()`](#asgiref.sync.async_to_sync) 装饰器，则该同步函数将成为主线程。

Thread-sensitive(线程敏感)模式非常特殊，在同一个线程中运行所有函数需要做很多工作。但是请注意，它依赖于堆栈中它上面的 [`async_to_sync()`](#asgiref.sync.async_to_sync) 的使用，以便在主线程上正确运行。如果你使用 `asyncio.run()` (或其他选项代替)，它将退回到单独共享线程（但不是主线程）中运行 thread-sensitive 函数。

在 Django 中需要这么做的原因是许多库，特别是数据库适配器，要求它们在创建时所在的同一个线程里对其进行访问，并且许多现有的 Django 代码假设它都在同一进程中运行（比如中间件将内容添加到请求中以供视图稍后使用）。

我们没有引入代码潜在的兼容性问题，而是选择了添加这种模式，以便所有现有的 Django 同步代码都在同一个线程中运行，从而完全兼容异步模式。注意，同步代码始终要与调用它的异步代码保持在不同线程中，所以你应该避免在任何你编写的新代码中传递原始数据库句柄(handles)或者其他 thread-sensitive 引用。
