异步自由线程的 Python
*********************

asyncio 使用事件循环作为调度器，通过在任务之间切换以允许非阻塞输入/输
出操作，从而实现高效的并发。这使得它在输入/输出范围用例中具有更好的性
能。它还允许将 CPU 密集型工作卸载到线程或进程池中，但这仍然受到
CPython 中 *全局解释器锁* 的限制。

然而，在 自由线程 Python 中，全局解释器锁被禁用，Python 可以运行真正的
多线程代码。这意味着 asyncio 现在可以利用多个 CPU 核心，而不受全局解释
器锁的限制。

从Python 3.14开始，asyncio对自由线程Python提供了一流的支持，并且
asyncio的实现可以在多线程环境中安全使用。

单个核心上的单个事件循环可以并发处理许多连接，但是用于处理每个连接的
Python 代码仍然串行执行。一旦请求涉及大量的每个请求计算，该处理就会成
为瓶颈，单个核心就无法跟上。在这里，将 asyncio 与线程结合使用最为有用
：通过每个线程运行一个事件循环，不同请求的处理可以在多个 CPU 核心上并
行运行。当你需要从 asyncio 应用程序运行阻塞或 CPU 密集型代码时，它也很
有用。

参见:

  在自由线程 Python 上扩展 asyncio，Kumar Aditya 撰写的博客文章，解释
  了为使 asyncio 在自由线程 Python 下安全且高效所做的内部变更，并附有
  相应改进效果的基准测试数据。


线程安全注意事项
================

虽然asyncio被设计为在自由线程Python环境中是线程安全的，但在将asyncio与
线程一起使用时，仍有一些注意事项需要记住：

1. **事件循环**：每个线程都应该有自己的事件循环，不应该在线程之间共享
   。这可以确保事件循环可以管理自己的任务和回调函数，而不会受到其他线
   程的干扰。

2. **任务管理**：在一个线程中创建的任务和未来不应等待或从另一个线程进
   行操作。

3. **线程安全 API**：当从多个线程与 asyncio 交互时，使用 asyncio 提供
   的线程安全 API 非常重要，例如 "asyncio.run_coroutine_threadsafe()"
   用于从另一个线程向事件循环提交协程。如果你需要从不同的线程调用回调
   函数，你可以使用 "loop.call_soon_threadsafe()" 来安全地调度它。

4. **同步**：asyncio 提供的同步原语（如 "asyncio.Lock" 和
   "asyncio.Event"）并非设计为跨线程使用。如果需要在线程之间同步，你应
   该改用 "threading" 模块中的同步原语。


在线程中使用asyncio
===================

asyncio 支持每个线程运行一个事件循环，这允许你在自由线程 Python 环境中
利用多个 CPU 核心。每个线程都可以运行自己的事件循环，任务可以在这些循
环上独立调度。

下面是如何在线程中使用异步的示例:

   import asyncio
   import threading

   async def worker(name: str) -> None:
       print(f"Worker {name} starting")
       await asyncio.sleep(1)
       print(f"Worker {name} done")

   def run_loop(name: str) -> None:
       asyncio.run(worker(name))

   threads = [
       threading.Thread(target=run_loop, args=(f"T{i}",))
       for i in range(4)
   ]
   for t in threads:
       t.start()
   for t in threads:
       t.join()

在这个例子中，每个线程都使用 "asyncio.run()" 创建自己的事件循环，并在
其上运行一个协程。这些线程并发执行，并且在自由线程构建版本中，它们可以
并行运行在不同的 CPU 核心上。


跨线程的生产者/使用者
=====================

当一个常规 (非 asyncio) 线程需要将工作交给在另一个线程中运行的 asyncio
事件循环时，请使用例如 "queue.Queue" 这样的线程安全原语，而不是
"asyncio.Queue"，后者仅在单个事件循环内是安全的。:

   import asyncio
   import queue
   import threading

   def producer(q: queue.Queue[int]) -> None:
       for i in range(5):
           print(f"Producing {i}")
           q.put(i)
       q.shutdown()

   async def consumer(q: queue.Queue[int]) -> None:
       while True:
           try:
               item = q.get_nowait()
           except queue.Empty:
               await asyncio.sleep(0.1)
               continue
           except queue.ShutDown:
               break
           print(f"Consumed {item}")
           await asyncio.sleep(item)

   q: queue.Queue[int] = queue.Queue()
   consumer_thread = threading.Thread(
       target=lambda: asyncio.run(consumer(q))
   )
   consumer_thread.start()
   producer(q)
   consumer_thread.join()

生产者在主线程上运行，而使用者在它自己的线程内的事件循环中运行，然而它
们通过 "queue.Queue" 安全地进行通信。当队列为空时，使用者会短暂休眠并
重试。当生产者完成时，它会调用 "shutdown()"，这会导致后续对
"get_nowait()" 的调用引发 "queue.ShutDown"，以便使用者可以干净地退出。
