Python並行処理シリーズ(V) - asyncioのコアコンセプトと基本アーキテクチャ
ステートメント この記事はpython 3.4以降のものです。3.4からasyncioが導入されたので、それ以降の3.5 3.6 3.7は前方互換性がありますが、構文は若干変更されています。比如在3.4版本中使用@asyncio.coroutine装饰器和yield from statement, but in 3.5 later versions use async, await two keywords instead, although the syntax is slightly different, but the principle is same.文法は少し変わっていますが、原理は同じです。この記事では、pythonasyncioの背後にある原則のいくつかを平易な言葉で説明します。 <スパン コアコンセプト を簡単に説明し asyncioのデザインアーキテクチャ のためのPythonの使用ガイドを提供します。 asyncioにおける非同期プログラミングのための一般的なテンプレート .
I. 最も重要な概念のいくつか
1. コンカレント(コルーチン) - 本来は関数である
いわゆる"コルーチン"は関数であり、関数は2つの基本的なコンポーネントを持っている必要があり、まず、需要使用@asyncio.coroutine进行装饰。第二に、関数本体は、発電機の戻り、または別の同時実行オブジェクトを返すためにから降伏の使用を持っている必要があります。
もちろん、この2つの条件は堅苦しいものではなく、これがなくても、普通の関数であることに変わりはない。
関数がコンカレントかどうか、どうやって見分けるのでしょうか?それは
asyncio.iscoroutine (obj)および
asyncio.iscoroutinefunction
(
ファンク
)
trueを返した後にyesを返す判定を追加する。
では、同時進行の関数は何をするのでしょうか?
(1)
result = yield from future
役割1.futureの結果を返す。futureとは?この文まで実行すると、コンカレント関数はfutureの結果が返されることを知りながら、中断している。もし、futureが途中でキャンセルされると、それがトリガーとなって
CancelledError exception.
後述するようにタスクはフューチャーのサブクラスなので、フューチャーのすべてのアプリケーションはタスクに等しく適用されます。
(2)
result = yield from coroutine
他の同時実行関数が結果を返すか、例外を発生させるまで待ちます。
(3) result= タスクから得られるもの
タスクの結果を返す
(4)
return expression
関数として、それ自体がある結果を返すことができる
(5)
raise exception
2. イベントループ - event_loop
同時実行関数は、直接実行すると呼ばれる通常の関数のように、イベントループに追加する必要がありますし、イベントループによって実行するには、単独で同時実行関数は、結果ではありません、簡単な例を参照してくださいの
import time
import asyncio
async def say_after_time(delay,what):
await asyncio.sleep(delay)
print(what)
async def main():
print(f"start time is: {time.time()}")
await say_after_time(1,"hello")
await say_after_time(2,"world")
print(f"end time is: {time.time()}")
loop=asyncio.get_event_loop() #Create event loop object
#loop=asyncio.new_event_loop() #Equivalent to the above, create a new event loop
loop.run_until_complete(main()) #Run a concurrent function through the event loop object
loop.close()
python 3.6では、通常の関数のように同時実行関数を単独で実行すると、以下のようにコルーチンオブジェクトのみが返されます(python 3.7)。
>>> main()
<coroutine object main at 0x1053bb7c8>
(1) イベントループオブジェクトを取得するいくつかの方法。
イベントループオブジェクトの取得、設定、作成は以下の方法で行うことができます。
loop=asyncio.
get_running_loop
() で現在のスレッドを返す(取得する)。
が実行されている
イベントループが存在しない場合はエラーになります。
loop=asyncio.
get_event_loop
() イベントループを取得し、現在のスレッドがまだイベントループを持っていない場合、新しいイベントループのループを作成します。
loop=asyncio.
set_event_loop
(
ループ
) は、イベントループを現在のスレッドのイベントループに設定します。
loop=asyncio.
new_event_loop
() 新しいイベントループを作成する
(2) イベントループで同時進行する関数を実行する2つの方法。
<スパン (1)モードI。 イベントループオブジェクトのループ、asyncio.get_event_loop()を作成し、イベントループを通して並列関数を実行する
(2) 方法2 asyncio.run(function_name)で直接並行関数を実行する。しかし、第一に、run 関数は Python 3.7 の新機能で、以前のバージョンにはないこと、第二に、run 関数は 常に新しいイベントループを作成し、実行後にそれを閉じる つまり、同じスレッドにすでにイベントループがある場合、この関数を再び使うことはできません。同じスレッドに2つのイベントループを持つことはできず、run関数はすでに1つ作成しているので、同時に2回実行することはできないからです。 つまり、同じスレッドに複数のイベントループは許されないのです。
asyncio.run()はpython 3.7で新しく追加されたAPIで、比較的低レベルのAPIであるasyncio.run_until_complete()との違いは後で取り上げますので、タスクを実行する方法として推奨します。
注意 イベントループとは一体何なのか?どう理解すればいいのでしょうか?
スレッドは様々な同時実行メソッドの間を止まることなくさまよい続け、yield fromやawaitに遭遇するとハングし、別のメソッドに行き、イベントループのすべてのメソッドが実行されるまで、このように理解することができるだろう。実際、loopはBaseEventLoopのインスタンスであり、その定義を見て、どのメソッドを呼び出さなければならないかを正確に確認することができます。
3, 待機可能なオブジェクトとは - つまり、待ち時間を一時停止できるオブジェクト
待ち受け可能なオブジェクトには、次の3種類があります。 コルーチン , タスク そして 先物 .
coroutineです。 は基本的に関数で、前のジェネレータfieldとfield fromに基づいて、さらに説明する必要はない。
タスク タスクとは、その名の通り、何かを達成するためのもので、実際には同時実行関数をさらに包むものである。
<スパン 未来へ ワンステップ操作の最終結果を表す"下位"概念で、一般にワンステップ操作は結果がすぐには得られないが、非同期の結果として"将来的には得られるという時間のかかる操作に用いられることからFutureと命名された。
の3つの関係 コルーチンは自動的にタスクにラップすることができ、タスクはFutureのサブクラスである。
4. タスクタスクとは
前述したように
Task
は、以下のように使用されます。
の同時スケジューリング
つまり、同時実行関数のさらなるラッピング?では、なぜラッピングが必要なのでしょうか?非同期プログラミングで最も重要なことは、非同期処理の状態を制御することです。
<スパン (1) タスクの作成(2つの方法) : は
<スパン 方法1. task = asyncio.create_task(coro()) # これは3.7で新たに追加されたものです。
方法2 task = asyncio.ensure_future(coro())
を使用することも可能です。
loop.
create_future
()
loop.
create_task
(
コロ
)
も可能です。
<スパン 注意事項 タスクの詳細は後日の連載記事で継続し、本記事はあくまで一般的な説明となります。
(2) 特定のタスクを取得するためのメソッド。
Method 1.
task=asyncio.
current_task
(
ループ=なし
)
指定されたループ内で現在実行中のタスク、または実行中のタスクがない場合はNoneを返します。
loopがNoneの場合、現在のイベントループで取得することをデフォルトとします。
Method two.
asyncio.
all_tasks
(
ループ=なし
)
ループ内の未終了のタスクを返す
5. 未来とは何ですか?
Futureは、非同期処理の最終結果を表す低レベルの待ちオブジェクトです。Futureオブジェクトが待ち受けられると、同時実行プロセスはFutureの演算が終了するまで待ちます。
FutureはTaskの親クラスです。一般に、両者の細かい違いを気にする必要はありませんし、Futureを使う必要もなく、Taskだけでいいのです。
未来のオブジェクトを返す低レベルの関数の良い例として
loop.run_in_executor()
.
<スパン II. asyncioの基本アーキテクチャ
前節ではasyncioの最もコアなコンセプトを紹介しました。これらのコンセプトをしっかり理解できれば、並行処理について学ぶのに非常に役立ちますが、私流に言うと、asyncioのアーキテクチャから紹介することにします。asyncioの設計アーキテクチャを理解することは、asyncioをより良く適用し、理解するのに役立ちます。
asyncioは高レベルAPIと低レベルAPIに分かれていて、どちらも使えるようになっています。先ほどmatplotlibのアーキテクチャの話をしたときに、先ほど話したコルーチンやタスクが高レベルAPIで、イベントループやフューチャーが低レベルAPIになります。もちろん、asyncioはそれ以上のものをカバーしているので、そこだけを見てみましょう。ここでは、高レベルAPIと低レベルAPIの概要を説明します。
ハイレベルAPI
低レベルAPI
- イベントループ <スパン (次回の記事で書きます)
- 先物
- トランスポートとプロトコル
- ポリシー
- プラットフォーム対応
高レベルのAPIとは、主に以下のようなものを指します。 asyncio.xxx() というメソッドがあります。
1. いくつかの一般的な高レベルのAPIメソッド
(1) Running an asynchronous concurrent process
asyncio.run
( コロ , * , debug=False ) # ワンステッププログラムの実行、上記参照
(2) タスクの作成
task=asyncio.
create_task
(
コロ
) #python3.7 ,上記参照
task = asyncio.ensure_future(coro())
(3)スリープ
await asyncio.
sleep
(
遅延
,
結果=なし
,
*
,
loop=None
)
この関数は、現在のタスク(同時実行関数)が他のタスクの実行を許可している間、どれくらいの時間スリープしているかを示します。これは、現在のスレッドを休ませるtime.sleep()との違いなので、注意しましょう。
また、パラメータresultを指定すると、現在のタスク(並行スレッド)が終了したときを返します。
loopパラメータは3.10で削除される予定なので、ここでは触れないことにします。
<スパン
(4) 複数のタスクの同時実行
await asyncio.
gather
(
*
coros_or_futures。
loop=None
,
return_exceptions=False
)
また、それ自体がawaitableである。
*coros_or_futures はシーケンス分割操作で、連結関数内にある場合は自動的にタスクに変換されます。
すべてのタスクが完了すると、*coros_or_futuresが完了したのと同じ順序の値を持つリストの形で結果が返される。
return_exceptions:False、これは彼のデフォルト値である。例外が発生した最初のタスクは直ちに戻り、残りのタスクは続行される。
True の場合、例外を発生させたタスクについても、そのタスクが正常に実行された場合と同様に、すべてのタスクの実行が終了するまで待機して、エラー結果を最終的な結果リストに返します。
gather()自体がキャンセルされた場合、それにバインドされているタスクもキャンセルされます。
<スパン (5) タスクキャンセルの防止
アウェイト
asyncio.
shield
(
*arg
,
*
,
loop=None
)
また、それ自体がawaitableである。shieldはその名の通り、遮蔽する、保護する、つまりawaitableなオブジェクトをキャンセルから守ることを意味しますが、これは一般的には推奨されず、処理中にtry文ブロックを使用する方が良いでしょう。
try:
res = await shield(something())
except CancelledError:
res = None
(6)タイムアウトの設定-よく理解されていること
待つ
asyncio.
wait_for
(
アワ
,
タイムアウト
,
*
,
loop=None
)
aw が並列関数である場合、自動的にタスクタスクにラップされます。
import asyncio
async def eternity():
print('I will start execution immediately')
await asyncio.sleep(3600) # The current task sleeps for 1 hour, or 3600 seconds
print('Finally, it's my turn')
async def main():
# Wait for at most 1 second
try:
print('Waiting for you for 3 seconds')
await asyncio.wait_for(eternity(), timeout=3) # Rest for 3 seconds to execute the task
except asyncio.TimeoutError:
print('Timeout!')
asyncio.run(main())
'''The result of the run is.
Waiting for you 3 seconds!
I'll start execution right away
Timeout!
'''
なぜ?最初の呼び出しmain()関数は、エントリ関数として、ときに出力'あなたのために3秒ああ待って'、メインハング、永遠を実行し、'私はすぐに実行を開始します'と表示し、永遠ハング、にその後永遠ハング、および3600秒、これは3よりも大きい、その時点で出発したタイムアウトエラー。 それを変更:''です。
import asyncio
async def eternity():
print('I'll start execution right away')
await asyncio.sleep(2) # current task sleeps for 2 seconds, 2<3
print('Finally, it's my turn')
async def main():
# Wait for at most 1 second
try:
print('Waiting for you for 3 seconds oh')
await asyncio.wait_for(eternity(), timeout=3) # Give you 3 seconds to execute your task
except asyncio.TimeoutError:
print('Timeout!')
asyncio.run(main())
'''The result of the run is.
Waiting for you 3 seconds!
I'll start executing right away
Finally it's my turn
'''
要約すると 非同期処理の実行時間がwaitforで設定したタイムアウトより長くなると、例外が発生します。ですから、プログラムを書くときに、非同期操作にタイムアウトを設定したい場合は、正しいものを選ぶようにしましょう。もし、非同期処理自体に時間がかかり、設定したタイムアウトが短すぎる場合、処理が完了する前に例外をスローすることになります。
<スパン (7) 複数同時実行時の待ち時間
アウェイト
asyncio.
wait
(
アワーズ
,
*
,
loop=None
,
タイムアウト=なし
,
return_when=ALL_COMPLETED
)
上記との違いは、第1引数のawsがコレクションセットとして記述するものである点、例えば
{func()、func()、func3()}。
は一連の並列関数またはタスクを表し、並列処理は自動的にタスクにラップされます。実際には、リストとして書いても問題ない。
<スパン 注意点 この関数の戻り値は、2つのタスク/フューチャーのセットである。
(done, pending)
where done is a collection of completed tasks and pending is a collection of tasks that have not yet been completed.
よくある使い方は、done, pending = await asyncio.wait(aws) です。
パラメータの説明です。
タイムアウト (a float or int), 上記と同じですが、これは asyncio.TimeoutError 例外が発生しないことに注意してください。タイムアウト時にまだ実行するタスクが残っている場合、それらのタスクとフューチャーは保留中のセカンドコレクションに返されます。
return_whenパラメータは、その名の通り、wait関数がいつ値を返すべきかを示すものです。以下の値だけが行くことができます。
<テーブル 定数 説明FIRST_COMPLETED
first_completes です。いずれかのタスクや未来が完了するか、キャンセルされると、wait関数は以下を返します。
FIRST_EXCEPTION
いずれかのタスクまたは未来が例外を発生させたとき、, を返します。どのタスクやフューチャーも例外をトリガーしなかった場合は、以下と同等となる。
ALL_COMPLETED
.
ALL_COMPLETED
すべてのタスクまたはフューチャーが完了またはキャンセルされたときに返す。
次の例に示すように
import asyncio
import time
a=time.time()
async def hello1(): # about 2 seconds
print("Hello world 01 begin")
yield from asyncio.sleep(2)
print("Hello again 01 end")
async def hello2(): # about 3 seconds
print("Hello world 02 begin")
yield from asyncio.sleep(3)
print("Hello again 02 end")
async def hello3(): # about 4 seconds
print("Hello world 03 begin")
yield from asyncio.sleep(4)
print("Hello again 03 end")
async def main(): #Entry function
done,pending=await asyncio.wait({hello1(),hello2(),hello3()},return_when=asyncio.FIRST_COMPLETED)
for i in done:
print(i)
for j in pending:
print(j)
asyncio.run(main()) # Run the entry function
b=time.time()
print('---------------------------------------')
print(b-a)
'''The result of the run is
Hello world 02 begin
Hello world 01 begin
Hello world 03 begin
Hello again 01 end
<Task finished coro=<hello1() done, defined at e:\Python learning\basic starter\asyncio3.4 details\test11.py:46> result=None>
<Task pending coro=<hello3() running at e:\Python learning\basic introduction\asyncio3.4 detailed\test11.py:61> wait_for=<Future pending cb=[< TaskWakeupMethWrapper object
at 0x000001FA8D394438>()]>>
<Task pending coro=<hello2() running at e:\Python learning\basic starter\asyncio3.4 detailed\test11.py:55> wait_for=<Future pending cb=[< TaskWakeupMethWrapper object
at 0x000001FA8D394378>()]>>
---------------------------------------
2.033155679702759
'''
上記からわかるように、hello1()は試運転を終えていますが、hello2()とhello3()は試運転を終えていません。
(8) asyncio.as_completed()関数
この関数の正しい中国語名がわからなかったので、ご存知の方がいらっしゃいましたら教えていただけると幸いです! この関数は以下のようなプロトタイプを持っています。
asyncio.
as_completed
(
アワーズ
,
*
,
loop=None
,
タイムアウト=なし
)
最初のパラメータaws: は、上記のようにコレクション{}で、コレクション内の要素はコルーチン、タスク、または未来です。
3番目のパラメータであるtimeoutは、上記と同じです。
具体的にどんなことをするのか?実にシンプルで、個人的には例から入ると、ちょっとチキンフィードな気がします。
import asyncio
import time
import threading
a=time.time()
@asyncio.coroutine
def hello1():
print("Hello world 01 begin")
yield from asyncio.sleep(5) # about 5 seconds
print("Hello again 01 end")
return 'Haha 1'
@asyncio.coroutine
def hello2():
print("Hello world 02 begin")
yield from asyncio.sleep(3) # about 3 seconds
print("Hello again 02 end")
return 'Haha 2'
@asyncio.coroutine
def hello3():
print("Hello world 03 begin")
yield from asyncio.sleep(4) # about 4 seconds
print("Hello again 03 end")
return 'Haha 3'
async def main():
s=asyncio.as_completed({hello1(),hello2(),hello3()})
for f in s:
result=await f
print(result)
asyncio.run(main())
b=time.time()
print('---------------------------------------')
print(b-a)
'''The result of the run is
Hello world 03 begin
Hello world 01 begin
Hello world 02 begin
Hello again 01 end
Haha 1
Hello again 02 end
Hello again 2
Hello again 03 end
Haha3
---------------------------------------
4.0225794315338135
'''
結論 asyncio.as_completed()関数はiterable(イテレータ)オブジェクトを返し、オブジェクトの各要素は未来のオブジェクトであり、多くのパートナーは、これは変化なしと同等ではないか、と言う。実際には、返された未来のコレクションのパラメータの再結合は、組み合わせの順序は、完了した並列関数(コルーチン、タスク、未来)の最初の実行は、上記のコードから見ることができる、パラメータがあります返すように最初にされています。
aws={hello1(),hello2(),hello3()}となります。 なぜならhello1は約5秒、hello2は約3秒、hello3は約4秒かかるからです。返される結果は
s={hello2(), hello3(), hello(1)}. は、hello2が最短でhello1が最長なので、最後に配置されます。次に、返された集合 s の反復処理を開始します。
2. Task クラスの詳細
まずは、Taskクラスについて簡単に紹介します(原文ママ)。
<ブロッククオートクラス
asyncio.
Task
(
コロ
,
*
,
loop=None
)
A
Future-like
Python を実行するオブジェクト
コルーチン
. スレッドセーフではありません。
タスクは、イベントループでコルーチンを実行するために使用されます。コルーチンが Future を待ち受ける場合、タスクはコルーチンの実行を一時停止し When the Future is the Future is the Future is the Future is the future を待ちます。 完了 の場合、ラップされたコルーチンの実行は再開される。
イベントループは協調スケジューリングを採用しており、一度に1つのタスクが実行されます。タスクがFutureの完了を待つ間、イベントループは他のタスクを実行します。タスクがFutureの完了を待つ間、イベントループは他のタスクやコールバックを実行したり、IOオペレーションを実行したりします。
高レベルの
asyncio.create_task()
関数でタスクを作成するか、低レベルの
loop.create_task()
または
ensure_future()
タスクの手動インスタンス化は推奨されません。
実行中のタスクをキャンセルするには
cancel()
これを呼び出すと、タスクに
CancelledError
キャンセル中にコルーチンがFutureオブジェクトで待機していた場合、Futureオブジェクトはキャンセルされます。
cancelled()
は、タスクがキャンセルされたかどうかを確認するために使用することができます。このメソッドは
True
を抑制しなかった場合、ラップされたコルーチンが
CancelledError
例外が発生し、実際にキャンセルされました。
asyncio.Task
を継承しています。
Future
を除くすべてのAPIは
Future.set_result()
と
Future.set_exception()
.
タスクは
contextvars
モジュールです。タスクが作成されると、現在のコンテキストをコピーし、後でコピーされたコンテキストでコルーチンを実行します。
上記の文章説明では、以下のようないくつかの非常に重要な情報が紹介されています。 は、ここで以下のようにまとめました。
(1) Pythonのconcurrentオブジェクトで、Futureオブジェクトによく似ていますが、スレッドセーフではありません。FutureのAPIをすべて継承していますが、Future.set_result() と Future.set_Exception() だけは継承しています。
(2) 高レベル API の asyncio.create_task() でタスクを作成するか、低レベル API の loop.create_task() または loop.ensure_future() でタスク・オブジェクトを作成する。
(3) 並列関数とは対照的に、タスクはステートフルであり、Task.cancel()を使用してキャンセルすることができます。この場合、CanceledError例外が発生し、キャンセルされたかを確認するためにcanceled()を使用します。
Taskクラスが使用する一般的な関数を以下に示します。
(1) cancel
()
タスクのキャンセルを要求する。
実際には、前述のように、彼が出発する例外CancelledErrorを使用するのが最善なので、キャンセルする必要がある同時実行関数内部のコードは、例外をトリガーして関連情報を印刷しやすいtry-except文ブロック内で行われますが、タスクがキャンセルされることを保証する方法がない一方で、フューチャー。cancel()はタスクがキャンセルされることを保証するものです。例として、次の例をご覧ください。
import asyncio
async def cancel_me():
print('cancel_me(): before sleep')
try:
await asyncio.sleep(3600) #Simulate a time-consuming task
except asyncio.CancelError:
print('cancel_me(): cancel sleep')
raise
finally:
print('cancel_me(): after sleep')
async def main():
# Create a task via a concurrent process. Note that when you create a task, you jump into asynchronous execution
#Because this is version 3.7, creating a task is equivalent to running the cancel_me function
task = asyncio.create_task(cancel_me())
#wait for one second
await asyncio.sleep(1)
print('The main function has finished resting')
#issue a request to cancel the task
task.cancel()
try:
await task #Exception is thrown because the task was cancelled
except asyncio.CanceledError:
print("main(): cancel_me is cancelled now")
asyncio.run(main())
'''The result of the run is.
cancel_me(): before sleep
The main function has finished its rest
cancel_me(): cancel sleep
cancel_me(): after sleep
main(): cancel_me is cancelled now
'''
プロセス解析を実行します。
まずrun関数がメイン関数エントリmainを起動し、mainでは、最初の文が非同期関数cancel_me()関数の呼び出しであるため、最初の文が先に出力される;その後に
次に cancel_me の try 文に進み、await に遭遇して一時停止し、この時点で main の実行に戻りますが、main で await に遭遇してこれも一時停止します。しかし main は 1 秒だけ一時停止すればよいのに対し、 cancel_me は 3600 秒も一時停止しなければならないため、 main の停止が終わるまで待ち、次に main を実行して 2 文目が出力されます。
次にタスクのキャンセル要求 task.cancel() に遭遇し、main 内で try を実行し続け、再び await に遭遇し、main が一時停止して cancel_me 関数に行きますが、タスクのキャンセル要求が main 内にあるので つまり、3600秒かかったタスクはもう実行されないのです という例外が発生し、3つ目の文が表示され、次に別の例外メッセージが表示されます。
次にcancel_meのfinishが実行され、4文目を表示し、その時点でcancel_meは実行を終了し、例外をスローしたため、メインプログラムmainに戻り、例外をトリガーし、5文目を表示します。
(2) done
()
ラップされた同時実行が例外を発生させず、キャンセルもされない場合、それは完了したことを意味し、真を返します。
(3) result
()
以下のタスクの実行結果を返します。
タスクが正しく実行された場合の結果を返します。
タスクがキャンセルされ、CancelledError例外が発生した場合、このメソッドを呼び出します。
タスクが無駄な結果を返したときにこのメソッドを呼び出すと、InvalidStateErrorが発生します。
タスクが例外を発生させて終了した場合、このメソッドを呼び出すと、プログラムを中断させた例外も再び発生します。
(4) exception
()
タスクが例外を発生させずに正常に実行された場合は None を返します。
タスクがキャンセルされたときにこのメソッドを呼び出すと、CancelledError例外が発生します。
タスクが終了していないときにこのメソッドを呼び出すと、InvalidStateError例外が発生します。
ここでは、一般的にはあまり使われないメソッドを、以下のように紹介します。
(5) add_done_callback
(
コールバック
,
*
,
context=None
)
(6) remove_done_callback
(
コールバック
)
(7) get_stack
(
*
,
limit=None
)
(8) print_stack
(
*
,
limit=None
,
ファイル=なし
)
(9) all_tasks
(
loop=None
) は、クラスメソッドである
(10) current_task
(
loop=None
) は、クラスメソッドである
3、非同期関数の結果を得るために
非同期プログラミング、非同期関数については、最も重要なことは、非同期関数の呼び出し後に、我々は関数の戻り値を取得する次の方法を持つことができます、最初は直接取得するタスクresult()を介して、第二は、コールバック関数を取得するバインドする、つまり、関数が非同期関数の戻り値を取得する関数を呼び出すには実行した後です。
<スパン (1)resultから直接取得する
import asyncio
import time
async def hello1(a,b):
print("Hello world 01 begin")
await asyncio.sleep(3) # Simulate time-consuming task for 3 seconds
print("Hello again 01 end")
return a+b
coroutine=hello1(10,5)
loop = asyncio.get_event_loop() # Step 1: Create event loop
task = asyncio.ensure_future(coroutine) #Step 2: Wrap multiple concurrent functions into a list of tasks
loop.run_until_complete(task) #Step 3: Run through the event loop
print('-------------------------------------')
print(task.result())
loop.close()
'''The result of the run is
Hello world 01 begin
Hello again 01 end
-------------------------------------
15
'''
(2)コールバック関数を定義して取得する。
import asyncio
import time
async def hello1(a,b):
print("Hello world 01 begin")
await asyncio.sleep(3) # Simulate time-consuming task for 3 seconds
print("Hello again 01 end")
return a+b
def callback(future): #Defined callback function
print(future.result())
loop = asyncio.get_event_loop() #Step 1: Create an event loop
task = asyncio.ensure_future(hello1(10,5)) #Step 2: Wrap multiple concurrent functions into tasks
task.add_done_callback(callback) # and bind a callback function to the task
loop.run_until_complete(task) #Step 3: Run through the event loop
loop.close() #Step 4: Close the event loop
'''The result of the run is.
Hello world 01 begin
Hello again 01 end
15
'''
注意事項 いわゆるコールバック関数とは、並列関数であるコルーチンの実行終了時にコールバック関数が呼び出されることを意味します。コールバック関数は、コルーチンの実行終了時に呼び出され、引数futureでコルーチンの実行結果を取得する。作成したタスクとコールバックのfutureオブジェクトは、タスクがfutureのサブクラスなので、実は同じオブジェクトなのです。
<スパン III. asyncio非同期プログラミングのための基本テンプレート
実際、asyncioを非同期プログラミングに使用する場合、構文形式は多様になることが多い。非同期プログラミングの核となる考え方を理解することは重要ですが、実装は結局のところステートメントの書き方次第です。今回与えられたテンプレートは2種類の例です。例1は3つの非同期メソッドで、いずれも引数なし、戻り値なし、時間のかかる作業をシミュレートしています。例2は3つの非同期メソッドで、いずれも引数あり、戻り値ありのものです。
1. Python 3.7より前のバージョン
(1) 例1:パラメータなし、戻り値なし
import asyncio
import time
a=time.time()
async def hello1():
print("Hello world 01 begin")
await asyncio.sleep(3) # Simulate a time-consuming task for 3 seconds
print("Hello again 01 end")
async def hello2():
print("Hello world 02 begin")
await asyncio.sleep(2) # Simulate time-consuming task for 2 seconds
print("Hello again 02 end")
async def hello3():
print("Hello world 03 begin")
await asyncio.sleep(4) # Simulate a time-consuming task for 4 seconds
print("Hello again 03 end")
loop = asyncio.get_event_loop() #Step 1: Create an event loop
tasks = [hello1(), hello2(),hello3()] #Step 2: Wrap multiple concurrent functions into a list of tasks
loop.run_until_complete(asyncio.wait(tasks)) #Step 3: Run through an event loop
loop.close() #Step 4: Cancel the event loop
'''The result of the run is.
Hello world 02 begin
Hello world 03 begin
Hello world 01 begin
Hello again 02 end
Hello again 01 end
Hello again 03 end
'''
(2) 例2:パラメータと戻り値のある場合
import asyncio
import time
async def hello1(a,b):
print("Hello world 01 begin")
await asyncio.sleep(3) # Simulate time-consuming task for 3 seconds
print("Hello again 01 end")
return a+b
async def hello2(a,b):
print("Hello world 02 begin")
await asyncio.sleep(2) # Simulate time-consuming task for 2 seconds
print("Hello again 02 end")
return a-b
async def hello3(a,b):
print("Hello world 03 begin")
await asyncio.sleep(4) # Simulate a time-consuming task for 4 seconds
print("Hello again 03 end")
return a*b
loop = asyncio.get_event_loop() #Step 1: Create an event loop
task1=asyncio.ensure_future(hello1(10,5))
task2=asyncio.ensure_future(hello2(10,5))
task3=asyncio.ensure_future(hello3(10,5))
tasks = [task1,task2,task3] # Step 2: Wrap multiple concurrent functions into a task list
loop.run_until_complete(asyncio.wait(tasks)) #Step 3: Run through an event loop
print(task1.result()) #and get the return value of the asynchronous function after all the tasks are completed
print(task2.result())
print(task3.result())
loop.close()
(3) まとめ:4つのステップ(python3.7以前のバージョン用)
<スパン ステップ1 -: イベントループを構築する
loop=asyncio.get_running_loop() # return (get) the event loop that is running in the current thread, or display an error if there is no running event loop; it is new in python 3.7
loop=asyncio.get_event_loop() # get an event loop, or create a new event loop loop if the current thread does not already have one.
loop=asyncio.set_event_loop(loop) # set an event loop to the current thread's event loop.
loop=asyncio.new_event_loop() # create a new event loop
ステップ 2: 1 つまたは複数の同時実行関数を TaskTask にラップする
# High-level API
task = asyncio.create_task(coro(argument list)) # This is a new addition in 3.7
task = asyncio.ensure_future(coro(argument_list))
# Low-level API
loop.create_future(coro)
loop.create_task(coro)
'''Note that while using Task.result() to get the result of the concurrent function, using asyncio.create_task() will show an error
But using asyncio.ensure_future is correct, I don't know why for now, which gods know, hope to tell, thank you very much!
ステップ3:イベントループを実行する
loop.run_until_complete(asyncio.wait(tasks)) #Integrate multiple tasks with asyncio.wait()
loop.run_until_complete(asyncio.gather(tasks)) #Integrate multiple tasks via asyncio.gather()
loop.run_until_complete(task_1) #Single task does not need to be integrated
loop.run_forever() #but this method has been deprecated in newer versions and is no longer recommended because it is not concise to use
'''
You can use gather or wait to register multiple tasks at the same time to achieve concurrency, but their design is completely different, as discussed in 2.1.(4) above, the main differences are as follows.
(1) The parameters are not the same
The parameters of gather are *coroutines_or_futures, i.e., in this form
tasks = asyncio.gather(*[task1,task2,task3]) or
tasks = asyncio.gather(task1,task2,task3)
loop.run_until_complete(tasks)
The arguments to wait are in the form of a list or a collection, as follows
tasks = asyncio.wait([task1,task2,task3])
loop.run_until_complete(tasks)
(2) The returned values are different
gather is defined as follows. gather returns the result of each task run
results = await asyncio.gather(*tasks)
wait is defined as follows, returning dones is the completed task, pending is the unfinished task, both are collection types
done, pending = yield from asyncio.wait(fs)
(3) We will talk about their further use later
'''
つまり、async.wait は done と pending の2つの値を返します。done は完了したタスク、pending はタイムアウトして future.result で呼び出す必要があるタスクです。async.gather は完了したタスクの結果を返します。
ステップ4:イベントループを閉じる
loop.close()
'''
None of the above examples call loop.close, and there doesn't seem to be a problem. So should we call loop.close or not?
Simply put, a loop can be run again as long as it is not closed: the
loop.run_until_complete(do_some_work(loop, 1))
loop.run_until_complete(do_some_work(loop, 3))
loop.close()
But if it is closed, it will no longer run: the
loop.run_until_complete(do_some_work(loop, 1))
loop.close()
loop.run_until_complete(do_some_work(loop, 3)) # Exception here
It is recommended to call loop.close to clean up the loop object completely to prevent misuse
'''
2. python バージョン 3.7
最新版のpython 3.7では、asyncioは以下のような新機能とAPIを導入しています。
(1) 例1:引数なし、戻り値なし
import asyncio
import time
async def hello1():
print("Hello world 01 begin")
await asyncio.sleep(3) # Simulate time-consuming task for 3 seconds
print("Hello again 01 end")
async def hello2():
print("Hello world 02 begin")
await asyncio.sleep(2) # Simulate time-consuming task for 2 seconds
print("Hello again 02 end")
async def hello3():
print("Hello world 03 begin")
await asyncio.sleep(4) # Simulate a time-consuming task for 4 seconds
print("Hello again 03 end")
async def main():
results=await asyncio.gather(hello1(),hello2(),hello3())
for result in results:
print(result) #None because no value is returned
asyncio.run(main())
'''The result of the run is.
Hello world 01 begin
Hello world 02 begin
Hello world 03 begin
Hello again 02 end
Hello again 01 end
Hello again 03 end
None
None
None
'''
(2) 例2:パラメータと戻り値のある場合
import asyncio
import time
async def hello1(a,b):
print("Hello world 01 begin")
await asyncio.sleep(3) # Simulate time-consuming task for 3 seconds
print("Hello again 01 end")
return a+b
async def hello2(a,b):
print("Hello world 02 begin")
await asyncio.sleep(2) # Simulate time-consuming task for 2 seconds
print("Hello again 02 end")
return a-b
async def hello3(a,b):
print("Hello world 03 begin")
await asyncio.sleep(4) # Simulate a time-consuming task for 4 seconds
print("Hello again 03 end")
return a*b
async def main():
results=await asyncio.gather(hello1(10,5),hello2(10,5),hello3(10,5))
for result in results:
print(result)
asyncio.run(main())
'''The result of the run is
Hello world 01 begin
Hello world 02 begin
Hello world 03 begin
Hello again 02 end
Hello again 01 end
Hello again 03 end
15
5
50
'''
(3) まとめ:2ステップ(python 3.7用)
ステップ1:エントリー機能mainの構築
彼はまた、非同期並行関数、すなわちasyncによって定義され、mainの内部で1つまたは複数の並行関数を待機する必要があります、以前のように、私はそれらを収集または待機によって組み合わせることができ、戻り値を持つ並行関数については、通常mainの内部で結果がフェッチされます。
ステップ2:メイン関数の起動 main
これはpython 3.7で追加された新しい関数で、1文だけ、つまりは
asyncio.run(main())
注意事項
実行関数の起動時に自動的に新しいイベントループが作成されるため、明示的にイベントループを作成する必要がなくなりました。そして、mainのイベントループからラップされた同時実行関数を削除する必要はありません。awaitキーワードを除いて、通常の関数と同様に呼び出すだけです。
<スパン IV. 並行プログラミングのメリット
1、CPUの時間共有スレッド保存コンテキスト問題がない(どのように並列コンテキストを保存するか)
2, ioブロッキングスイッチに遭遇 (実装方法)
3、共有データなしの保護ロック(なぜ)
4. 次回の連載の予告 - 低レベルAPIの紹介、イベントループが実際にどのように実装されているか、futureクラスの実装を紹介します。
関連
-
Python 人工知能 人間学習 描画 機械学習モデル作成
-
TclError: 表示名がない、$DISPLAY環境変数がない問題を解決しました。
-
[解決済み] Asyncioです。タスクの例外が取得されないという奇妙な事態が発生
-
[解決済み] Pythonの円形リストイテレータ
-
[解決済み] matplotlibのレイヤーの順番を指定する
-
[解決済み] Windows 10にKivyをインストールする際の問題点
-
[解決済み] ImportError: externという名前のモジュールがない
-
[解決済み] Pythonのアトミック操作とは?
-
[解決済み] Python-降雨統計
-
エラー処理 TypeError: initial_value must be unicode or None, not str
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Flaskアプリで "could not locate flask application "というエラーが発生する。Flask Migrateで「......FLASK_APP環境変数」のエラーが発生する。
-
[解決済み] Pylint の無効な定数名
-
[解決済み] PhantomJS with Seleniumのエラーです。メッセージ 'phantomjs' 実行ファイルが PATH にある必要があります。
-
[解決済み] PythonでPDFを特定のプリンタに無音印刷する
-
[解決済み] Windows 10 の conda が内部コマンドまたは外部コマンドとして認識されない
-
[解決済み] ダグランを埋め戻す際の気流を防ぐには?
-
[解決済み] Pythonで日付の文字列形式を検証するにはどうすればよいですか?
-
データフレームの基本
-
Pythonのリクエストで "Max retries exceeded with url "というエラーが発生する。
-
[Python Learning] [scikit-learn] Pipeline reports error fit_transform() takes 2 positional arguments but were 3 given.