[英]python asyncio migrate from 3.4 to 3.5+
Good evening everybody, I am trying to create internet robot and I met the problem while migrating my script from python 3.4 to 3.5 or 3.6+. 大家晚上好,我试图创建互联网机器人,但在将脚本从python 3.4迁移到3.5或3.6+时遇到了问题。 It uses asyncio and works good on 3.4 python but when I start it with python3.5+ I got error:
RuntimeError: Cannot run the event loop while another loop is running
它使用asyncio并在3.4 python上运行良好,但是当我使用python3.5 +启动它时出现错误:
RuntimeError: Cannot run the event loop while another loop is running
here is the code scheme: 这是代码方案:
import multiprocessing as mp
import asyncio
import concurrent.futures
import aiohttp
def create_proccesses(separate_loop_creator, coro):
proccesses = []
for n in range(2):
proc = mp.Process(target=separate_loop_creator, args=(coro,))
proc.start()
proccesses.append(proc)
for p in proccesses:
p.join()
def separate_loop_creator(coro):
sep_loop = asyncio.new_event_loop()
asyncio.set_event_loop(sep_loop)
tasks = [asyncio.async(coro(sep_loop)) for _ in range(100)]
try:
sep_loop.run_until_complete(asyncio.wait(tasks))
sep_loop.close()
except Exception as err:
print(err)
for task in tasks:
task.cancel()
sep_loop.close()
@asyncio.coroutine
def manager(exe, loop):
# some calculations and start coros in several processes
loop.run_in_executor(
exe,
create_proccesses,
separate_loop_creator,
some_coro
)
@asyncio.coroutine
def some_work_in_mainloop():
while True:
print('Some server dealing with connections here...')
yield from asyncio.sleep(1)
@asyncio.coroutine
def some_coro(loop):
with aiohttp.ClientSession(loop=loop) as session:
response = yield from session.get('http://google.com')
yield from asyncio.sleep(2)
print(response.status)
if __name__ == '__main__':
mainloop = asyncio.get_event_loop()
executor = concurrent.futures.ProcessPoolExecutor(5)
asyncio.async(some_work_in_mainloop())
asyncio.async(manager(executor, mainloop))
try:
mainloop.run_forever()
finally:
mainloop.close()
The exception raise in separate_loop_creator()
coroutine and it is RuntimeError: Cannot run the event loop while another loop is running
. 异常在eparate_loop_creator
separate_loop_creator()
协程中引发,它是RuntimeError: Cannot run the event loop while another loop is running
。 I think it is because of changing get_event_loop()
mechnics, but I do not understand what is wrong with my code. 我认为这是因为更改了
get_event_loop()
,但我不明白我的代码有什么问题。
Here is what I want to do: 这是我想做的:
+--------------+
+-------+other service |
+----------+ +--------------+
| mainloop |
+----------+
| +------------+
+-----+ executor |
+------+-----+
|
+------+--------+
|start proccess |
+---+-------+---+
+-----------------+ | | +---------------+
|start new loop +------+ +------+ start new loop|
+--------+--------+ +-------+-------+
| |
+-------+-------+ +------v-------+
| run coro | | run coro |
+---------------+ +--------------+
Here is trace which I get on python3.5.3: 这是我在python3.5.3上获得的跟踪:
Traceback (most recent call last):
File "tst.py", line 21, in separate_loop_creator
sep_loop.run_until_complete(asyncio.wait(tasks))
File "/root/.pyenv/versions/3.5.3/lib/python3.5/asyncio/base_events.py", line 454, in run_until_complete
self.run_forever()
File "/root/.pyenv/versions/3.5.3/lib/python3.5/asyncio/base_events.py", line 411, in run_forever
'Cannot run the event loop while another loop is running')
RuntimeError: Cannot run the event loop while another loop is running
Cannot run the event loop while another loop is running
Traceback (most recent call last):
File "tst.py", line 21, in separate_loop_creator
sep_loop.run_until_complete(asyncio.wait(tasks))
File "/root/.pyenv/versions/3.5.3/lib/python3.5/asyncio/base_events.py", line 454, in run_until_complete
self.run_forever()
File "/root/.pyenv/versions/3.5.3/lib/python3.5/asyncio/base_events.py", line 411, in run_forever
'Cannot run the event loop while another loop is running')
RuntimeError: Cannot run the event loop while another loop is running
Python 3.4.3 results: Python 3.4.3结果:
...
200
Some server dealing with connections here...
200
200
Some server dealing with connections here...
200
200
Some server dealing with connections here...
200
...
This is actually a bug in asyncio in CPython 3.6.0. 这实际上是CPython 3.6.0中asyncio中的错误。 There's a PR to fix this, so that 3.6.1 works as expected.
有一个PR可以解决此问题,以便3.6.1可以正常工作。
As a workaround you can add the following piece of code in your project: 解决方法是,可以在项目中添加以下代码:
import sys
if sys.version_info[:3] == (3, 6, 0):
import asyncio.events as _ae
import os as _os
_ae._RunningLoop._pid = None
def _get_running_loop():
if _ae._running_loop._pid == _os.getpid():
return _ae._running_loop._loop
def _set_running_loop(loop):
_ae._running_loop._pid = _os.getpid()
_ae._running_loop._loop = loop
_ae._get_running_loop = _get_running_loop
_ae._set_running_loop = _set_running_loop
The best solution, if possible, try removing multiprocessing
altogether from your program, and use only one event loop (Optionally using ProcessPoolExecutor
for isolated CPU intensive tasks). 最好的解决方案,如果可能,请尝试从
multiprocessing
完全删除multiprocessing
,并仅使用一个事件循环(对于独立的CPU密集型任务,可以选择使用ProcessPoolExecutor
)。
As of 2017-03-02, there is an open python bug for this issue, effecting non-windows platforms: https://bugs.python.org/issue22087 . 截至2017年3月2日,此问题有一个打开的python错误,影响到非Windows平台: https : //bugs.python.org/issue22087 。
Here is a shorter program to trigger the same problem: 这是触发相同问题的较短程序:
import asyncio
import multiprocessing as mp
def create_another_loop():
loop = asyncio.new_event_loop()
loop.run_forever()
async def create_process():
proc = mp.Process(target=create_another_loop)
proc.start()
proc.join()
if __name__ == '__main__':
main_loop = asyncio.get_event_loop()
main_loop.run_until_complete(create_process())
main_loop.close()
A hackish workaround (Caution! Use at your own risk!) inspired from the fix suggested here https://github.com/python/asyncio/pull/497 is to add this code to a newly created Process
: 受https://github.com/python/asyncio/pull/497此处建议的修复启发,一个变黑的解决方法(警告!使用后果自负!)是将此代码添加到新创建的
Process
:
if asyncio.events._running_loop:
asyncio.events._running_loop._loop = None
Example: 例:
import asyncio
import multiprocessing as mp
import time
from concurrent.futures.process import ProcessPoolExecutor
async def clock(label, n=5, sleep=1):
print(label, "start")
for i in range(n):
await asyncio.sleep(sleep)
print(label, i + 1)
print(label, "end")
return label
def create_another_loop():
# HACK START
if asyncio.events._running_loop:
asyncio.events._running_loop._loop = None
# HACK END
loop = asyncio.new_event_loop()
loop.run_until_complete(clock("sub"))
loop.close()
def create_process():
time.sleep(2)
proc = mp.Process(target=create_another_loop)
proc.start()
proc.join()
return "ok"
async def create_process_in_pool():
return await main_loop.run_in_executor(ProcessPoolExecutor(), create_process)
if __name__ == '__main__':
main_loop = asyncio.get_event_loop()
tasks = (
clock("main"),
create_process_in_pool(),
)
print(main_loop.run_until_complete(asyncio.gather(*tasks)))
main_loop.close()
Other possible workarounds: Create the processes before starting the loop or use asyncio.create_subprocess_exec
which even allows to communicate with the subprocess via a stream . 其他可能的解决方法:在开始循环之前创建进程,或使用
asyncio.create_subprocess_exec
甚至允许通过流与子进程进行通信 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.