asyncio.subprocess总是阻塞

时间:2017-03-03 17:10:10

标签: python python-3.x python-asyncio

我需要编写一些异步代码,它将子进程作为其任务的一部分运行。即使我使用asyncio.subprocess,我的代码仍然是阻塞的。我的服务器看起来像这样:

getBounds()

一个非常基本的客户:

导入asyncio 导入websockets

import asyncio
import asyncio.subprocess
import websockets

async def handler(websocket, path):
    while True:
        data = await websocket.recv()
        print('I received a message')
        player = await asyncio.create_subprocess_exec(
            'sleep', '5',
            stdin=asyncio.subprocess.DEVNULL,
            stdout=asyncio.subprocess.DEVNULL,
            stderr=asyncio.subprocess.DEVNULL)
        await player.wait()
        print('Finished waiting')

server = websockets.serve(handler, '0.0.0.0', '8000')
asyncio.get_event_loop().run_until_complete(server)
asyncio.get_event_loop().run_forever()

我希望输出看起来像这样:

async def client():
    async with websockets.connect('ws://localhost:8000') as websocket:
        for i in range(5):
            await websocket.send('message')
            await asyncio.sleep(0.5)

asyncio.get_event_loop().run_until_complete(client())

但我得到了这个:

I received a message
I received a message
I received a message
I received a message
I received a message
Finished waiting
Finished waiting
Finished waiting
Finished waiting
Finished waiting

每次“我收到一条消息”后,等待5秒钟。

2 个答案:

答案 0 :(得分:2)

await player.wait()行不会阻止其他异步操作,但会等待5秒!

如果您不想等待回复,请尝试改为使用ensure_future()

# add:
async def wait_for_player(player, path):
    print("Waiting...", path)
    await player.wait()
    print("Done", path)

# and replace await player.wait() with:
asyncio.ensure_future(wait_for_player(player, path))

您实际上也可以将create_subprocess_exec()移至wait_for_player()

要查看您的代码没有阻止,请参阅以下内容:

客户端:

import asyncio

import websockets


async def client(n):
    async with websockets.connect('ws://localhost:8000/{}/'.format(n)) as websocket:
        print(n, "start")
        for i in range(5):
            print(n, i)
            await websocket.send('message')
            await asyncio.sleep(0.5)
    print(n, "done")


tasks = [client(i) for i in range(5)]
asyncio.get_event_loop().run_until_complete(asyncio.wait(tasks))

服务器:

import asyncio
import asyncio.subprocess

import random
import websockets


async def handler(websocket, path):
    try:
        while True:
            data = await websocket.recv()
            pause = random.randint(1, 5)
            print('I received a message', path, "Pausing:", pause)
            player = await asyncio.create_subprocess_exec(
                'sleep', str(pause),
                stdin=asyncio.subprocess.DEVNULL,
                stdout=asyncio.subprocess.DEVNULL,
                stderr=asyncio.subprocess.DEVNULL)
            await player.wait()
            print('Finished waiting', path)
    except websockets.ConnectionClosed:
        print("Connection closed!", path)


server = websockets.serve(handler, '0.0.0.0', '8000')
asyncio.get_event_loop().run_until_complete(server)
asyncio.get_event_loop().run_forever()

答案 1 :(得分:0)

您的ws服务器似乎没问题。实际上是你的客户端阻塞了。如果要测试服务器的异步行为,则需要进行异步请求。客户端中的for循环阻塞该线程。所以删除它,然后使用asyncio.gather以异步方式运行client()方法

import asyncio
import websockets

async def client():
    async with websockets.connect('ws://localhost:8000') as websocket:
        await websocket.send('message')
        await asyncio.sleep(0.5)


tasks = asyncio.gather(*[client() for i in range(5)])
asyncio.get_event_loop().run_until_complete(tasks)