なぜPython協業の使い方がまだ分かりませんか?
6660 ワード
私は思想のあるプログラム猿について、一生実践者を勉強します。現在は創業チームでteam leadを担当しています。技術スタックはAndroid、Python、JavaとGoに関連しています。これも私達のチームの主要な技術スタックです。Github:https://github.com/hylinux1024 WeChat公衆号:生涯開発者(angrycode)
前の編の「Python反復可能(Iterable)」、「シーケンサ(Iterator)およびジェネレータ(Generator)の概念」の文では、ジェネレータ(リストジェネレータ は、 を使用する。
0 x 00は協程とは何ですか?
プロセス(
協働の定義
0 x 01 Awaitableオブジェクト
協働は
タスク(
3.Future
通常、アプリケーション層コードにおいては、
1.Task
前に私たちは
2.ギャザー
0 x 03参照https://docs.python.org/3/library/asyncio-task.html
前の編の「Python反復可能(Iterable)」、「シーケンサ(Iterator)およびジェネレータ(Generator)の概念」の文では、ジェネレータ(
Generator
)が以下の2つの方法で定義されていることが分かる。yield
によって定義された関数Python
の初期バージョンでの協働は、ジェネレータによっても達成され、すなわちジェネレータベースの協働(Generator-based Coroutines)である。前の記事では、ジェネレータの記事の終わりに生産者-消費者の例を挙げて、ジェネレータの協働に基づいて実現されます。def producer(c):
n = 0
while n < 5:
n += 1
print('producer {}'.format(n))
r = c.send(n)
print('consumer return {}'.format(r))
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('consumer {} '.format(n))
r = 'ok'
if __name__ == '__main__':
c = consumer()
next(c) # consumer
producer(c)
このコードを見て、多くの初心者が私と同じようにジェネレータによる協働を実現するのは難しいと思います。すぐに業務によって自分の協働コードを書くことができます。Python
実装者たちもこの問題に注目しています。生成器ベースの協働も廃棄されるので、本明細書ではPythonic
パケットの使用と関連するいくつかの関連する概念について重点的に紹介する。私が使っているasyncio
環境は3.7です。0 x 00は協程とは何ですか?
プロセス(
Python
)はスレッド内で実行され、マイクロスレッドとして理解されるが、プロセスの切り替えにはコンテキストの消費がなく、スレッドよりも軽い。一つの協働はいつでも中断されてもいいです。他の協働プロセスを実行させてもいいし、中断から回復して実行してもいいです。その間のスケジュールはプログラマによって制御されます。協働の定義
Coroutine
3.5+バージョンにPython
とaysnc
のキーワードが追加されました。この2つのシンタックスキャンディーは私たちにとても便利に定義して使用しています。関数定義時にawait
で宣言して協働プロセスを定義した。import asyncio
#
async def simple_async():
print('hello')
await asyncio.sleep(1) # 1
print('python')
# asynio run
asyncio.run(simple_async())
#
# hello
# python
協働中に他の協働を呼び出す場合はasync
を使用する。await
のキーワードはawait
で定義された関数で使用されるべきであり、逆にasync
関数はasync
でなくてもよい。#
async def simple_async():
print('hello')
asyncio.run(simple_async())
#
# hello
await
は、入ってきたプロセスを実行し、asyncio.run()
イベントサイクルを管理する。asyncio
方法が協働を直接実行できる以外に、イベントサイクルrun()
を使用してもよい。async def do_something(index):
print(f'start {time.strftime("%X")}', index)
await asyncio.sleep(1)
print(f'finished at {time.strftime("%X")}', index)
def test_do_something():
#
task = [do_something(i) for i in range(5)]
#
loop = asyncio.get_event_loop()
# task
loop.run_until_complete(asyncio.wait(task))
loop.close()
test_do_something()
#
# start 00:04:03 3
# start 00:04:03 4
# start 00:04:03 1
# start 00:04:03 2
# start 00:04:03 0
# finished at 00:04:04 3
# finished at 00:04:04 4
# finished at 00:04:04 1
# finished at 00:04:04 2
# finished at 00:04:04 0
ほぼ同時に協働が開始されたとみられます。ソースコードを読むと、loop
の実装もasyncio.run()
オブジェクトとその呼び出しをパッケージ化していることが分かります。loop
は、毎回、協働を実行するための新しいイベントサイクルオブジェクトを作成する。0 x 01 Awaitableオブジェクト
asyncio.run()
において待機可能なオブジェクトは、協働(Python
)、タスク(Awaitable
)、corountine
である。すなわち、これらのオブジェクトは、Task
キーワードを使用して起動することができる。await awaitable_object
1.協働(Coroutine)協働は
Future
によって定義され、もう一つの協働はawait
を使用して呼び出されることができる。async def nested():
print('in nested func')
return 13
async def outer():
# await
print(await nested())
asyncio.run(outer())
#
# in nested func
# 13
async def
方法でawait
を直接呼び出した場合、outer()
を使用せずにnested()
を呼び出すと、await
を抛り出すだろう。async def outer():
# , coroutine
nested()
asyncio.run(outer())
プログラムを実行すると、コンソールから以下の情報が出力されます。RuntimeWarning: coroutine 'nested' was never awaited
nested()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
2.タスク(Task)タスク(
RuntimeWarning
)は、協働を同時に実行するために使用されることができる。Task
を使用して、1つの協働オブジェクトをタスクにカプセル化することができ、このタスクはすぐにスケジュール・キューに配置されて実行される。async def nested():
print('in nested func')
return 13
async def create_task():
# create_task ,
task = asyncio.create_task(nested())
# task
# await ,
ret = await task
print(f'nested return {ret}')
asyncio.run(create_task())
#
# in nested func
# nested return 13
注:合併については、以下にも詳しく説明します。3.Future
asyncio.create_task()
は、非同期動作の最終結果である特別な低レベル(Future
)オブジェクトである。Futureオブジェクトが待機している場合、このプロセスはFutureオブジェクトが他の場所で動作するまで待機することを意味します。通常、アプリケーション層コードにおいては、
low-level
オブジェクトが直接に作成されない。いくつかのライブラリおよびeventual result
モジュールでは、オブジェクトに使用されます。async def used_future_func():
await function_that_returns_a_future_object()
0 x 02合併1.Task
前に私たちは
Future
が同時に実行できることを知っています。asyncio
は協働プロセスをTask
にパッケージする方法です。async def do_after(what, delay):
await asyncio.sleep(delay)
print(what)
# asyncio.create_task
async def corun():
task1 = asyncio.create_task(do_after('hello', 1)) # 1
task2 = asyncio.create_task(do_after('python', 2)) # 2
print(f'started at {time.strftime("%X")}')
# , ,
await task1
await task2
print(f'finished at {time.strftime("%X")}')
asyncio.run(corun())
#
# started at 23:41:08
# hello
# python
# finished at 23:41:10
asyncio.create_task()
は1秒を実行するタスクであり、Task
は2秒を実行するタスクであり、2つのタスクを同時に実行し、合計2秒を消費する。2.ギャザー
task1
を使用する以外に、task2
を使用してもよく、この方法は、協働パラメータリストを受信する。async def do_after(what, delay):
await asyncio.sleep(delay)
print(what)
async def gather():
print(f'started at {time.strftime("%X")}')
# gather
await asyncio.gather(
do_after('hello', 1),
do_after('python', 2),
)
print(f'finished at {time.strftime("%X")}')
asyncio.run(gather())
#
# started at 23:47:50
# hello
# python
# finished at 23:47:52
二つのタスクが費やす時間は、その中で最も長いタスクです。0 x 03参照