1- #!/usr/bin/env python3
2- # -*- coding: utf-8 -*-
31import asyncio
42import atexit
53import threading
64import weakref
75
8- from typing import Awaitable , Callable , TypeVar
6+ from functools import wraps
7+ from typing import Any , Awaitable , Callable , Coroutine , TypeVar
98
109T = TypeVar ('T' )
1110
1211
1312class _TaskRunner :
14- """A task runner that runs an asyncio event loop on a background thread. """
13+ """在后台线程上运行 asyncio 事件循环的任务运行器 """
1514
1615 def __init__ (self ):
1716 self .__loop : asyncio .AbstractEventLoop | None = None
@@ -20,48 +19,55 @@ def __init__(self):
2019 atexit .register (self .close )
2120
2221 def close (self ):
23- """关闭事件循环 """
22+ """关闭事件循环并清理 """
2423 if self .__loop :
2524 self .__loop .stop ()
25+ self .__loop = None
26+ if self .__thread :
27+ self .__thread .join ()
28+ self .__thread = None
29+ name = f'TaskRunner-{ threading .get_ident ()} '
30+ _runner_map .pop (name , None )
2631
2732 def _target (self ):
28- """后台线程目标"""
29- loop = self .__loop
33+ """后台线程的目标函数"""
3034 try :
31- loop .run_forever ()
35+ self . __loop .run_forever ()
3236 finally :
33- loop .close ()
37+ self . __loop .close ()
3438
35- def run (self , coro ) :
36- """在后台线程上同步运行协程 """
39+ def run (self , coro : Awaitable [ T ]) -> T :
40+ """在后台事件循环上运行协程并返回其结果 """
3741 with self .__lock :
38- name = f'{ threading .current_thread (). name } - runner '
42+ name = f'TaskRunner- { threading .get_ident () } '
3943 if self .__loop is None :
4044 self .__loop = asyncio .new_event_loop ()
4145 self .__thread = threading .Thread (target = self ._target , daemon = True , name = name )
4246 self .__thread .start ()
43- fut = asyncio .run_coroutine_threadsafe (coro , self .__loop )
44- return fut .result (None )
47+ future = asyncio .run_coroutine_threadsafe (coro , self .__loop )
48+ return future .result ()
4549
4650
4751_runner_map = weakref .WeakValueDictionary ()
4852
4953
50- def run_await (coro : Callable [..., Awaitable [T ]]) -> Callable [..., T ]:
51- """将协程包装在一个函数中,该函数会阻塞 ,直到它执行完为止"""
54+ def run_await (coro : Callable [..., Awaitable [T ]] | Callable [..., Coroutine [ Any , Any , T ]] ) -> Callable [..., T ]:
55+ """将协程包装在函数中,该函数将在后台事件循环上运行 ,直到它执行完为止"""
5256
57+ @wraps (coro )
5358 def wrapped (* args , ** kwargs ):
54- name = threading .current_thread ().name
5559 inner = coro (* args , ** kwargs )
60+ if not asyncio .iscoroutine (inner ) and not asyncio .isfuture (inner ):
61+ raise TypeError (f'Expected coroutine, got { type (inner )} ' )
5662 try :
57- # 如果当前此线程中正在运行循环
58- # 使用任务运行程序
63+ # 如果事件循环正在运行,则使用任务调用
5964 asyncio .get_running_loop ()
65+ name = f'TaskRunner-{ threading .get_ident ()} '
6066 if name not in _runner_map :
6167 _runner_map [name ] = _TaskRunner ()
6268 return _runner_map [name ].run (inner )
6369 except RuntimeError :
64- # 如果没有,请创建一个新的事件循环
70+ # 如果没有,则创建一个新的事件循环
6571 loop = asyncio .get_event_loop ()
6672 return loop .run_until_complete (inner )
6773
0 commit comments