web-dev-qa-db-ja.com

asyncioによるノンブロッキングI / O

Pygameとasyncioを使用してネットワーク化されたゲームを作成しようとしていますが、読み取りにハングアップしないようにする方法がわかりません。これがクライアントのための私のコードです:

_@asyncio.coroutine
def handle_client():
    print("Connected!")
    reader, writer = yield from asyncio.open_connection('localhost', 8000)
    while True:
        mouse_up = False
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()                
            Elif event.type == pygame.MOUSEBUTTONUP:
                mouse_up = True

        if mouse_up:
            print("Writing")
            writer.write(b"Mouse up")
        print("Waiting to read")
        line = yield from reader.read(2**12)
        print(line.decode())

    writer.close()
_

これはline = yield from reader.read(2**12)の行にぶら下がっています。以前、asyncioのポイントは非ブロッキングであると考えていたため、読み取るデータがない場合は実行を継続します。そうではないことがわかりました。

AsyncioネットワークコードをPygameの描画およびイベントコードと統合するにはどうすればよいですか?

20
rlms

_yield from_のポイントは、実行をasyncioのイベントループに切り替えることですand結果が利用可能になるまで、現在のコルーチンをブロックします。現在のコルーチンをブロックせずにタスクをスケジュールするには、asyncio.async()を使用できます。

Pygameループをブロックせずにこれまでのデータを印刷するには:

_@asyncio.coroutine
def read(reader, callback):
    while True:
        data = yield from reader.read(2**12)
        if not data: # EOF
            break
        callback(data)

@asyncio.coroutine
def echo_client():
    reader, ...
    chunks = []
    asyncio.async(read(reader, chunks.append))
    while True:
        pygame.event.pump() # advance pygame event loop
        ...
        if chunks: # print read-so-far data
            print(b''.join(chunks).decode())
            del chunks[:]
        yield from asyncio.sleep(0.016) # advance asyncio loop
_

whileループ内にブロッキング呼び出しがあってはなりません。

read()およびsleep()コルーチンは、同じスレッドで同時に実行されます(もちろん、他のコルーチンも同時に実行できます)。

9
jfs

ブロッキングタスクを非ブロッキングタスクに「変換」できます。

私はこれを提案します: https://docs.python.org/3/library/asyncio-eventloop.html#executor

Twitterフィードをリッスンする関数、「メンション」関数があり、エグゼキュータで実行しているので、ハングしても他のタスクはブロックされません。

@asyncio.coroutine
def boucle_deux():
#faire attendre la boucle si pas bcp de mots
    while True:
        print("debut du deux")
        value = t.next()
        future2 = loop.run_in_executor(None, mention, "LQNyL2xvt9OQMvje7jryaHkN8",
                                       "IRJX6S17K44t8oiVGCjrj6XCVKqGSX9ClfpGpfC467rajqePGb",
                                       "2693346740-km3Ufby8r9BbYpyzcqwiHhss22h4YkmnPN4LnLM",
                                       "53R8GAAncFJ1aHA1yJe1OICfjqUbqwcMR38wSqvbzsQMB", 23, value)
        response2 = yield from future2
        yield from asyncio.sleep(5)
        print("fin du deux")

asyncio.Task(boucle_deux())
7
Morgan

read()を呼び出した直後に「line」の値を読み取ろうとしているので、どんな犠牲を払ってもその値が必要です...

データがないためにコルーチンが停止しない場合、「line」がNoneの場合、line.decode()呼び出しでAttributeErrorが発生する可能性があります。

実行できることの1つは、ブロッキング呼び出しにタイムアウトを設定し、タイムアウト例外を処理することです。

...
print("Waiting to read")
try:  # block at most for one second
    line = yield from asyncio.wait_for(reader.read(2**12), 1)
except asyncio.TimeoutError:
    continue
else:
    print(line.decode())
...
0
Danny