web-dev-qa-db-ja.com

Pythonで可能な最も単純なasync / awaitの例

asyncio/async/awaitについての多くの例、ブログ投稿、質問/回答を読んだことがありますPython 3.5+、複雑で、私が見つけた最も単純なものはおそらく this one です。それでも_ensure_future_を使用しており、Pythonの非同期プログラミングについて学習するために、さらに最小限の例が可能かどうかを確認したいと思います。 (つまり、基本的な非同期/待機の例を行うために必要な最小限のツールは何ですか)。

質問:Pythonの非同期プログラミングについて学習する目的で、async/awaitがどのように機能するかを示す単純な例を与えることは可能ですか、これらの2つのキーワードのみを使用して+ asyncio.get_event_loop() + _run_until_complete_ + other Python code but but no asyncio function?

例:このようなもの:

_import asyncio

async def async_foo():
    print("async_foo started")
    await asyncio.sleep(5)
    print("async_foo done")

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget async_foo()
    print('Do some actions 1')
    await asyncio.sleep(5)
    print('Do some actions 2')

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
_

ただし、_ensure_future_はなく、await/asyncの動作方法を示しています。

18
Basj

これらの2つのキーワード+ asyncio.get_event_loop() + run_until_complete +その他Pythonのみを使用して、async/awaitの動作を示す簡単な例を示すことは可能ですかコードはあるが他のasyncio関数はない?

このようにして、動作するコードを書くことができます。

import asyncio


async def main():
    print('done!')


if __name__ ==  '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

しかし、この方法ではasyncioが必要な理由を示すことは不可能です。

ところで、なぜ単純なコードだけでなくasyncioが必要なのですか?答えは-asyncioは、I/Oブロッキング操作(ネットワークへの読み取り/書き込みなど)を並列化するときにパフォーマンスの利点を得ることができます。また、便利な例を書くには、これらの操作の非同期実装を使用する必要があります。

詳細な説明については、 この回答 をお読みください。

更新:

oK、ここではasyncio.sleepを使用してI/Oブロッキング操作を模倣し、asyncio.gatherを使用して複数のブロッキング操作を同時に実行する方法を示す例を示します。

import asyncio


async def io_related(name):
    print(f'{name} started')
    await asyncio.sleep(1)
    print(f'{name} finished')


async def main():
    await asyncio.gather(
        io_related('first'),
        io_related('second'),
    )  # 1s + 1s = over 1s


if __name__ ==  '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

出力:

first started
second started
first finished
second finished
[Finished in 1.2s]

両方のio_relatedがどのように開始されたかに注意してください。

16

あなたの質問に答えるために、同じ問題に対する3つの異なる解決策を提供します。

ケース1:ちょうど普通のpython

import time

def sleep():
    print(f'Time: {time.time() - start:.2f}')
    time.sleep(1)

def sum(name, numbers):
    total = 0
    for number in numbers:
        print(f'Task {name}: Computing {total}+{number}')
        sleep()
        total += number
    print(f'Task {name}: Sum = {total}\n')

start = time.time()
tasks = [
    sum("A", [1, 2]),
    sum("B", [1, 2, 3]),
]
end = time.time()
print(f'Time: {end-start:.2f} sec')

出力:

Task A: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task A: Sum = 3

Task B: Computing 0+1
Time: 2.01
Task B: Computing 1+2
Time: 3.01
Task B: Computing 3+3
Time: 4.01
Task B: Sum = 6

Time: 5.02 sec

ケース2:async/await done wrong

import asyncio
import time

async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    time.sleep(1)

async def sum(name, numbers):
    total = 0
    for number in numbers:
        print(f'Task {name}: Computing {total}+{number}')
        await sleep()
        total += number
    print(f'Task {name}: Sum = {total}\n')

start = time.time()

loop = asyncio.get_event_loop()
tasks = [
    loop.create_task(sum("A", [1, 2])),
    loop.create_task(sum("B", [1, 2, 3])),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

end = time.time()
print(f'Time: {end-start:.2f} sec')

出力:

Task A: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task A: Sum = 3

Task B: Computing 0+1
Time: 2.01
Task B: Computing 1+2
Time: 3.01
Task B: Computing 3+3
Time: 4.01
Task B: Sum = 6

Time: 5.01 sec

ケース3:async/await done rightsleep関数を除いてケース2と同じ)

import asyncio
import time

async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    await asyncio.sleep(1)

async def sum(name, numbers):
    total = 0
    for number in numbers:
        print(f'Task {name}: Computing {total}+{number}')
        await sleep()
        total += number
    print(f'Task {name}: Sum = {total}\n')

start = time.time()

loop = asyncio.get_event_loop()
tasks = [
    loop.create_task(sum("A", [1, 2])),
    loop.create_task(sum("B", [1, 2, 3])),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

end = time.time()
print(f'Time: {end-start:.2f} sec')

出力:

Task A: Computing 0+1
Time: 0.00
Task B: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task B: Computing 1+2
Time: 1.00
Task A: Sum = 3

Task B: Computing 3+3
Time: 2.00
Task B: Sum = 6

Time: 3.01 sec

case 1case 2は同じ5 secondsを与えますが、case 33 secondsだけです。したがって、async/await done rightは高速です。

違いの理由は、sleep関数の実装内です。

# case 1
def sleep():
    print(f'Time: {time.time() - start:.2f}')
    time.sleep(1)

# case 2
async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    time.sleep(1)

# case 3
async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    await asyncio.sleep(1)

case 1およびcase 2sleep関数は「同じ」です。他の人にリソースの使用を許可せずに「スリープ」します。一方、case 3は、リソースがスリープ状態のときにリソースへのアクセスを許可します。

case 2では、asyncを通常の関数に追加しました。ただし、イベントループは中断せずに実行します。どうして?ループが関数を中断して別のタスクを実行することを許可されていないためです。

case 3では、別のタスクを実行するために関数を中断する場所をイベントループに正確に伝えました。正確にはどこで?

# case 3
async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    await asyncio.sleep(1) # <-- Right here!

詳細はこちらをご覧ください こちら

10
Levon