当我在 Python 3.7 中运行此代码时:
import asyncio
sem = asyncio.Semaphore(2)
async def work():
async with sem:
print('working')
await asyncio.sleep(1)
async def main():
await asyncio.gather(work(), work(), work())
asyncio.run(main())
Run Code Online (Sandbox Code Playgroud)
它因运行时错误而失败:
$ python3 demo.py
working
working
Traceback (most recent call last):
File "demo.py", line 13, in <module>
asyncio.run(main())
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
return future.result()
File "demo.py", line 11, in main
await asyncio.gather(work(), work(), work())
File "demo.py", line 6, in work
async with sem:
File …Run Code Online (Sandbox Code Playgroud) 在我的代码中,我有一个带有属性的类,偶尔需要运行异步代码。有时我需要从异步函数访问属性,有时从同步函数访问属性 - 这就是为什么我不希望我的属性是异步的。此外,我的印象是异步属性通常是一种代码异味。如果我错了纠正我。
我在从同步属性执行异步方法并阻止进一步执行直到异步方法完成时遇到问题。
这是一个示例代码:
import asyncio
async def main():
print('entering main')
synchronous_property()
print('exiting main')
def synchronous_property():
print('entering synchronous_property')
loop = asyncio.get_event_loop()
try:
# this will raise an exception, so I catch it and ignore
loop.run_until_complete(asynchronous())
except RuntimeError:
pass
print('exiting synchronous_property')
async def asynchronous():
print('entering asynchronous')
print('exiting asynchronous')
asyncio.run(main())
Run Code Online (Sandbox Code Playgroud)
它的输出:
entering main
entering synchronous_property
exiting synchronous_property
exiting main
entering asynchronous
exiting asynchronous
Run Code Online (Sandbox Code Playgroud)
首先,RuntimeError捕获似乎是错误的,但如果我不这样做,我会得到RuntimeError: This event loop is already running例外。
其次,asynchronous()函数最后执行,同步完成后。我想通过异步方法对数据集进行一些处理,所以我需要等待它完成。如果我await asyncio.sleep(0)在调用之后添加synchronous_property() …
如何在Quart中的另一个异步方法内调用在主线程中获取事件循环的异步方法?
t.py
from telethon import TelegramClient, functions, types
client2 = TelegramClient(sn, api_id, api_hash).start()
async def create_contact():
return await client2(functions.contacts.ImportContactsRequest([
types.InputPhoneContact(0, '8', 'first_name', 'last_name')
]))
Run Code Online (Sandbox Code Playgroud)
应用程序
from quart import Quart, websocket,render_template,request
import t2
app = Quart(__name__)
@app.route('/wa2tg')
def wa2tg():
return render_template('wa2tg.html',nm=request.args.get('nm',''))
@app.websocket('/wa2tg2')
async def wa2tg2():
while True:
data = await websocket.receive()
await t2.create_contact()
# Thread(target=tele.client2.run_until_disconnected).start()
app.run(debug=1)
Run Code Online (Sandbox Code Playgroud)
错误:
Running on http://127.0.0.1:5000 (CTRL + C to quit)
[2019-06-21 16:31:42,035] 127.0.0.1:51696 GET /wa2tg 1.1 200 553 12995
[2019-06-21 16:31:42,486] 127.0.0.1:51698 GET /wa2tg2 1.1 101 …Run Code Online (Sandbox Code Playgroud) 我想使用一组类别执行网页抓取,每个类别还有一个 URL 列表。所以我决定在主函数中只根据每个类别来调用一个函数,并且在内部函数内有一个非阻塞调用。
所以这是代码:
def main():
loop = asyncio.get_event_loop()
b = loop.create_task(f("p", all_p_list))
f = loop.create_task(f("f", all_f_list))
loop.run_until_complete(asyncio.gather(p, f))
Run Code Online (Sandbox Code Playgroud)
它应该同时执行该f函数。
但该f函数还必须运行循环,因为在该函数中它根据每个 URL 同时调用一个函数。
async def f(category, total):
urls = [urls_template[category].format(t) for t in t_list]
soups_coro = map(parseURL_async, urls)
loop = asyncio.get_event_loop()
result = await loop.run_until_complete(asyncio.gather(*soups_coro))
Run Code Online (Sandbox Code Playgroud)
但是运行脚本后,出现错误This event loop is already running,我发现这是因为我loop.run_until_complete()同时调用了内部函数和外部函数。
但是,当我剥离run_until_complete(), 并仅调用f()时main(),函数调用立即完成,并且无法等待内部函数完成。所以不可避免的要调用循环中的main(). 但后来我认为它与内部函数不兼容,内部函数也必须调用它。
我该如何处理这个问题并运行循环?原始代码全部相同main()并且有效,但如果可能的话我想使其更清晰。
作为我上一个关于从同步函数调用异步函数的问题的后续,我发现了asyncio.run_coroutine_threadsafe。
从纸面上看,这看起来很理想。根据StackOverflow 问题中的评论,这看起来很理想。我可以创建一个新线程,获取对原始事件循环的引用,并安排异步函数在原始事件循环内运行,同时仅阻塞新线程。
class _AsyncBridge:
def call_async_method(self, function, *args, **kwargs):
print(f"call_async_method {threading.get_ident()}")
event_loop = asyncio.get_event_loop()
thread_pool = ThreadPoolExecutor()
return thread_pool.submit(asyncio.run, self._async_wrapper(event_loop, function, *args, **kwargs)).result()
async def _async_wrapper(self, event_loop, function, *args, **kwargs):
print(f"async_wrapper {threading.get_ident()}")
future = asyncio.run_coroutine_threadsafe(function(*args, **kwargs), event_loop)
return future.result()
Run Code Online (Sandbox Code Playgroud)
这不会出错,但也不会返回。期货只是挂起,异步调用永远不会被命中。call_async_method我是否在、_async_wrapper或两者中使用 Future 似乎并不重要;无论我在哪里使用 Future,它都会挂起。
我尝试将调用run_coroutine_threadsafe直接放入主事件循环中:
event_loop = asyncio.get_event_loop()
future = asyncio.run_coroutine_threadsafe(cls._do_work_async(arg1, arg2, arg3), event_loop)
return_value = future.result()
Run Code Online (Sandbox Code Playgroud)
未来也悬在这里。
我尝试使用此处LoopExecutor定义的类,这似乎完全满足了我的需求。
event_loop = asyncio.get_event_loop()
loop_executor …Run Code Online (Sandbox Code Playgroud)