第二に、綿密asyncioコルーチン(タスクオブジェクト、同時コルーチンの原則を呼び出しコルーチン)

  それが明確に存在し発現させることができるので、すべて自分の前にメモを書く、ブログを書き始めたので、あまりにも、などの問題、のこんにちは品種をったらしい質問や批判コメントエリアに歓迎されています。

あなたは新しいコルーチンがある場合は、上の記事をお読みくださいコルーチンasyncio知人を予備的な理解を持ってasyncioします。

まず、タスク・オブジェクト(タスク割り当て)

参照知人asyncioコルーチンは、タスク・オブジェクトは、様々な状態、例えば状態(一時停止)をブロックとして、運転状態(走行)、完了ステータス(DONE)を含むコルーチンのカプセル化であることを理解します。

1. 3つの方法でタスクオブジェクトを作成します。

  • 最初:loop.create_task(xxx)

  • 第二:asyncio.ensure_future(xxx)

  • 第三:asyncio.create_task(xxx)

    実際には、第一の方法は、しかし、この方法では、タスクを作成する前に実行されたイベントのサイクルを必要とする、または投げることに注意され、第三の方法の内部で使用されていますRuntimeError:no running event loop

2、コールバックを追加

import asyncio
async def get_url(url):
    print('start get_url')
    await asyncio.sleep(2)
    print('end get_url')
    return 'Joshua'

def callback(future):  # 回调函数
    print('Hello {}'.format(future.result()))

if __name__=="__main__":
    loop = asyncio.get_event_loop()
    task = loop.create(get_url('https://www.baidu.com'))
    task.add_done_callback(callback)  # 添加回调
    loop.run_until_complete(task)

図3は、コールバック関数にパラメータを渡します

from functools import partial
# partial(偏函数)可以把函数包装成另外一个函数
import asyncio
async def get_url(url):
    print('start get_url')
    await asyncio.sleep(2)
    print('end get_url')
    return 'Joshua'

def callback(url,future):
    # 注意:要想向回调函数传递参数需要将参数放在future前面
    print('Hello {}'.format(future.result()))

if __name__=="__main__":
    loop = asyncio.get_event_loop()
    task = loop.create(get_url('https://www.baidu.com'))
    # 在传递前需要先用partial将函数封装成另外一个函数
    task.add_done_callback(partical(callback,'https://www.baidu.com'))

図4に示すように、タスクキャンセル()

次に、イベントループの次のラウンドでスローされるキャンセルタスクオブジェクトを、要求しますCanceledError

  • リクエストのキャンセル:task.cancel()

  • キャンセルするかどうかを確認します。cancelled()

  • 終了するかどうかを確認します。 ` done()`

  • たとえば、次のよう

    import asyncio
    
    async def a():
        print('执行a()')
        await asyncio.sleep(3)
        print('执行a()完成')
    
    async def b():
        print('执行b()')
        await asyncio.sleep(2)
        print('执行b()完成')
    
    if __name__ == '__main__':
        loop = asyncio.get_event_loop()
        task1 = loop.create_task(a())
        task2 = loop.create_task(b())
        task1.cancel()  # 取消task1
        loop.run_until_complete(asyncio.gather(task1, task2))
    
    # 运行结果如下
    >>> 执行b()
    >>> Traceback (most recent call last):
      ... # 省略Traceback中的部分
    >>> concurrent.futures._base.CancelledError
    # 如果不想因为抛出异常而中断运行,可以在`gather`中设置`return_exception=True`
    # 如果设置`return_exception=True`,这样异常会和成功的结果一样处理,并聚合至结果列表,那么运行结果将如下:
    >>> 执行b()
    >>> 执行b()完成

    私たちは見ることができますa()実行されません。

5、キャンセルシールドシールド

asyncio.shield AW **、ループ=なし)、オブジェクトを保護するための待機が解除されます

  • AW:コルーチンが内部タスクとしてパッケージされる場合オブジェクトは、待つことができます

私たちは、次のように改正する上記の最初の例をキャンセル:

import asyncio

async def a():
    print('执行a()')
    await asyncio.sleep(3)
    print('执行a()完成')

async def b():
    print('执行b()')
    await asyncio.sleep(2)
    print('执行b()完成')

def c():
    loop = asyncio.get_event_loop()
    task1 = loop.create_task(a())
    task1 = asyncio.shield(task1)  # 设置屏蔽取消操作
    task2 = loop.create_task(b())
    task1.cancel()
    loop.run_until_complete(asyncio.gather(task1, task2, return_exceptions=True))

c()
# 运行结果如下
>>> 执行a()
>>> 执行b()
>>> 执行b()完成

キャンセルされた、まだここを参照してください、少しの友人は確かに(砂)の顔(刻まれた)無知(ボー)力(メイン)ですが、それは本当にああ私を責めないでください、それは本当に、なぜそれがある、出力されます?私たちは、見てshield()、次のように、ソースコード:

def shield(arg, *, loop=None):
    inner = ensure_future(arg, loop=loop)  # 首先创建一个内部的Task 赋给inner
    if inner.done():  # 如果内部的Task执行完毕则返回inner
        # Shortcut.
        return inner
    loop = futures._get_loop(inner)
    outer = loop.create_future()  # 在事件循环上创建一个外部的Task

    def _done_callback(inner):  # 回调函数,在inner执行完毕后回调
        if outer.cancelled():  # 如果外部Task被取消
            if not inner.cancelled():  # 如果内部Task没被取消
                # Mark inner's result as retrieved.
                inner.exception()  # 根据注释的意思,内部Task的结果会被标记为已检索
            return

        if inner.cancelled():  # 如果内部的Task取消了,则外部的Task也将取消
            outer.cancel()  
        else:
            exc = inner.exception()  # 返回内部Task的异常
            if exc is not None:  # 如果存在异常,则外部Task的异常被设置为内部Task的异常
                outer.set_exception(exc)
            else:  # 否则将外部Task结果设置为内部Task的结果
                outer.set_result(inner.result())

    inner.add_done_callback(_done_callback)
    return outer  # 返回外部Task

実質内部タスク(内部)、内部タスクから来ているイベントループ上のすべての外部タスクを実行し、上記のソースから見た、と我々はそれがないタスクの内側には、イベントループの外部タスクでキャンセルすることができます影響、内部はまだどのようにそれを解決すること、しかし、我々はシールドを設定したり、キャンセルされたが、キャンセルにつながったタスクを、実行している間ので、すべてのタスクの実行は、イベントループを抜けるに完了しているのですか?

次の二つの方法:

  • キャンセルされる通常よりも消費少ない消費キャンセルタスクタスク、聞かせてa()コルーチンが遅延より設定ブロッキングシミュレートb()小コルーチンので、イベントループ以外のマスク解除コルーチン内部タスクを:次のように、通常のタスクの実行が早期に完了さ

    async def a():
        print('执行a()')
        await asyncio.sleep(1)  # 由原来的3改为1,使其小于b()的延时
        print('执行a()完成')
    
    async def b():
        print('执行b()')
        await asyncio.sleep(2)
        print('执行b()完成')

    次のように実行結果は以下のとおりです。

    >>> 执行a()
    >>> 执行b()
    >>> 执行a()完成
    >>> 执行b()完成

    これは下で見ることができますa()完了コルーチン実行。

  • イベントループの実行が継続的に、それはタスクのイベントループの実行が完了したことがないだろうに、内部タスクにつながるプログラムの終了がキャンセルコルーチンをマスクしてみましょう実装を完了するために失敗しました。次のように:

    import asyncio
    import time
    async def a():
        print('执行a()')
        await asyncio.sleep(3)
        print('执行a()完成')
    
    async def b():
        print('执行b()')
        await asyncio.sleep(2)
        print('执行b()完成')
    
    if __name__ == '__main__':
        loop = asyncio.get_event_loop()
        task1 = loop.create_task(a())
        task2 = loop.create_task(b())
        loop.run_forever()
    # 输出如下
    >>> 执行a()
    >>> 执行b()
    >>> 执行b()完成
    >>> 执行a()完成

タスクオブジェクトの6、他の方法は、(公式文書から貼り付け)

  • result()

    タスクの結果を返します。タスクオブジェクトがあれば完了し、その結果、そのパケットは、コルーチンに返される(またはコルーチンが例外をスローする場合、例外が再開始されます。)タスクオブジェクトがあればキャンセルされ、この方法は、発生させCancelledErrorた例外を。タスクオブジェクトの結果が利用できない場合、このメソッドは、発生させInvalidStateErrorた例外を。

  • exception()

    例外Taskオブジェクトを返します。パケットコルーチンで例外が発生した場合、例外が返されます。パケットコルーチンが正常に返された場合はこの方法が返されますNoneTaskオブジェクトが場合はキャンセルされ、この方法は、発生させCancelledErrorた例外を。Taskオブジェクトがされていない場合は終了し、この方法は、スローInvalidStateError例外を。

  • remove_done_callbackコールバック

    コールバックのリストから削除コールバック指定したコールバック。この方法は、低レベルのコールバックベースのコードで使用されるべきです。詳細については、を参照してくださいFuture.remove_done_callback()文書を。

  • get_stack(** 制限=なし*)

    このスタックフレームは、Taskオブジェクトのリストを返します。パケットがコルーチンを完了していない場合は、どこにその保留スタックに戻ります。コルーチンが正常に完了またはキャンセルされた場合は、空のリストを返します。コルーチンが異常終了した場合には、フレームリストを戻ります。プレスフレームから古いから新しい順に常にあります。各懸濁コルーチンは、唯一のスタックフレームを返します。オプションの制限パラメータは、戻りフレームの最大数を指定し、すべてのフレームを返すデフォルト。オーダーのリストが返却またはスタックトレースに依存返します:最も古いフレームを返すために戻って、最新のスタックフレームを返します。(これは、動作のトレースバックモジュールと一致しています。)

  • print_stack(** 制限=なしファイル=なし*)

    このタスクスタックオブジェクトやバックトラックを印刷します。この方法では、トレースバックモジュールのような出力生成しget_stack()、取得したフレーム。制限引数はに直接渡されますget_stack()ファイルのパラメータが書かれたI / Oストリームの出力で、デフォルトの出力が書き込まれますsys.stderr

  • クラスメソッド all_tasksループ=なし

    イベントループのすべてのタスクのコレクションを返します。デフォルトでは、すべてのタスクの現在のイベントループを返します。場合はループがありNone、使用されるget_event_loop()現在のイベントループ機能を得ること。方法であり、この非推奨とPython 3.9。での使用になりますによって除去asyncio.all_tasks()INSTEAD機能。

  • クラスメソッド current_taskループ=なし

    現在のタスクまたは実行を返しますNone場合はループがありNone、使用されるget_event_loop()現在のイベントループ機能を得ること。

第二に、原則はコルーチンを呼び出し

より良いバックの同時実行を理解するために、今私たちはコルーチンの呼び出しの下原理を理解する必要があり、最初のコルーチンネストされた例を与えます:

import asyncio
import time
async def compute(x,y):
    await asyncio.sleep(1)
    return x+y

async def print_sum(x,y):
    reuslt = await compute(x,y)
    print(result)

if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(print_sum(1,2))
    loop.close()
    print('Cost:{}'.format(time.time()-start))
    
#输出如下
>>> 3
>>> Cost:1.0007200241088867

以下は、この例のタイミングチャートです。

  1. 作成Event Loopイベントループを呼び出すrun_until_completeイベントループを開始しするprint_sumタスクオブジェクトのタスクを作成し、Event Loop実行している状態に入り、タスクが保留状態になります。

  2. タスク駆動輸送print_sumする操作await compute(x,y)コールサブコルーチンをシフトcomputeprint_sum入力します

    サスペンド状態では、サブコルーチンは、compute走行状態に入ります。

  3. compute実行await asyncio.sleep(1)時間のかかるブロッキングの発生、サスペンド状態に入る、この時間は、タスクと直接通信、及び(呼び出し側タスクにサブコルーチン)作業チャネルを開くために、概ねサブスレッド通信とプロセスと同様に理解することができます。

    これの利点は、確実にするためであるprint_sumと同時に行われ、全体的なプログラムは、問題を説明するための例に続いて、非同期に実行されます。

  4. コルーチンと電源を入れた後、子とのタスク通信Event Loopの通信を、Event Loopブロック操作が完了するために他のタスクが存在しないだけで(スリープ1秒)待機します。

  5. 時間のかかる操作が完了ブロッキングで待った後、Event Loopタスクサブコルーチンによりcomputeチャネルウェイクアップをcompute、次にcompute実行状態に入ります。

  6. computeコード次のステップreturn x+yスローながら、状態から走行状態に行う、StopIteration例外およびキャリーreturn結果を、

  7. print_sum捕捉computeスローされた例外、およびそこから取得するcompute結果が返され、このときprint_sum実行状態にサスペンド状態から、コードの次のステップprint(result)

  8. print_sum実行print後の変換状態、走行状態によって行われ、スローStopIteration例外を、

  9. タスク捕捉print_sum後の状態によって行わruningて異常状態に投げ込ま。

  10. この場合にはEvent Loop停止状態に入ります。

例えば、第3条について説明します。

import asyncio
import time
async def compute(x,y):
    await asyncio.sleep(1)
    return x+y

async def print_sum(x,y):
    result = await compute(x,y)
    print(result)

if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    tasks = [print_sum(1,2),print_sum(1,2)]  # 创建两个协程对象
    loop.run_until_complete(asyncio.gather(*tasks))  # 同时执行两个协程对象
    loop.close()
    print('Cost:{}'.format(time.time()-start))
                            
#输出如下
>>>3
>>>3
>>>Cost:1.0004866123199463

上記の例から分かるように、オブジェクトのときに稼働時間コルーチン複数行われていない、実際には、asyncio.gatherアクションの一部は、オブジェクトコルーチンタスクをオンにし、イベントループにそれらを登録することですこの例では、タイミングチャート説明上方3,4のステップが待機をブロックされていない、別の呼び出しが、オンprint_sum、タスクのある非同期動作ので、この方法は、理論的にはまだのみ処理1000個のコルーチンの実行オブジェクトであっても1秒。

例を見てみましょう:

import asyncio
import time
async def compute(x,y):
    print('执行compute')
    await asyncio.sleep(1)
    return x+y

async def print_sum(x,y):
    result1 = await compute(x, y)  # 调用子协程
    print(result1)
    result2 = await compute(x, y)  # 调用子协程
    print(result2)

if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(print_sum(1,2))  # 同时执行两个协程对象
    loop.close()
    print('Cost:{}'.format(time.time()-start))
# 输出如下
>>> 执行compute
>>> 3
>>> 执行compute
>>> 3
>>> Cost:2.0158169269561768

この場合、あなたはことを確認し、2秒の全体的な実行時間を見ることができprint_sumますが、それはまだ方法を変更するために第2の1である完了したい場合は、同期が実行されているので、時間がかかるを倍増二つのサブコルーチンの実装?

自然は、タスク・オブジェクト(タスク)、および以下にコルーチンオブジェクトをさせ、非同期動作に同期動作です。

import asyncio
import time
async def compute(x,y):
    print('执行compute')
    await asyncio.sleep(1)
    return x+y

async def print_sum(x,y):
    task1 = asyncio.create_task(compute(x, y))
    task2 = asyncio.create_task(compute(x, y))
    await task1
    print(task1.result())
    await task2
    print(task2.result())

if __name__ == "__main__":
    start = time.time()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(print_sum(1,2))  
    loop.close()
    print('Cost:{}'.format(time.time()-start))
# 输出如下
>>> 执行compute
>>> 执行compute
>>> 3
>>> 3
>>> Cost:1.0004889965057373

これはピットがあるでは以下のように、我々は、書面で避けるように注意を払います:

async def print_sum(x,y):
    await asyncio.create_task(compute(x, y))
    await asyncio.create_task(compute(x, y))
    # 这样写依然是同步运行,要将生成的对象赋给一个变量再进行await,即上个例子那样

三、asyncio同時

原則として上記のコルーチンを呼び出すいくつかの事例では、我々は実際に同時の知識部分へのアクセス権を持っている、のは、同時実行asyncio詳細に説明しましょう。

いくつかの方法によって複雑asyncio:

  1. asyncio.gather(* AWS、ループ=なし、return_exceptions = False)が `

    • aws:あなたがオブジェクト(コルーチン、タスク、未来)を待つことができ、オブジェクトがコルーチンであれば、その後、待つことができasynico.gather内部は自動的にタスクに変換し、イベントループを追加します。すべてのオブジェクトが正常に完了待っていれば、結果は同じ重合からのすべての戻り値のリストであり、AWS順番ます。
    • return_exceptionsそれはデフォルト値である場合にはFalse、オブジェクトがキャンセルされた場合、AWSは待つことができ、その後、スローCancelledErrorプログラムの実行が終了し、それが設定されている場合はTrue、例外がスローされると、プログラムの正常な動作は、リスト内の結果を返しますこの結果は待つようにオブジェクトもconcurrent.futures._base.CancelledError()
  2. asyncio.wait((aws, *, loop=None, timeout=None, return_when=ALL_COMPLETED)

    • AWS:オブジェクトがAWSは、指定されたターゲットを待ってから会うまでスレッドをブロックすることが同時に実行、待つことができreturn_when指定された条件を、

    • ループ:このパラメータは、それを無視することはできません、それは公式の文書に従って説明するpython3.10には、削除します。

    • タイムアウト:あなたがコントロールを返す前に待機する最大秒数に適用される設定し、つながることはありません場合は、小数と整数をサポートしてTimeoutErrorタイムアウトが未完成のタスクまたは将来のリターンを発生した場合、。

    • return_when:条件指定されたタイミング機能を返します。

      FIRST_COMPLETED:最初のオブジェクトの待機完了を返します。

      FIRST_EXCEPTION:例外復帰の最初の発生、異常が完了したすべてのオブジェクトを待っている間に返されない場合。

      ALL_COMPLETED:デフォルトでは、すべてのオブジェクトが完了したときに返すために待っています

asyncio.gatherそして、asyncio.waitの違い:

  1. 両方が同時コルーチン、のために使用されるasyncio.gather演算結果のコルーチンを返すasyncio.wait(コルーチンとコルーチン二つのリストが完了していないが完了した)は、2つのタスク/将来のリストを返します。

  2. asyncio.gather全体のブラックボックスに相当し、ちょうどあなたのコルーチンの結果を伝えるために。

  3. asyncio.gatherタスクグループは、例えば、であってもよいです。

    import asyncio
    async def get_url(url):
       print('start get_url')
       await asyncio.sleep(2)
       print('end get_url')
       return 'Joshua'
    
    if __name__=="__main__":
       loop = asyncio.get_event_loop()
       # 将url分组
       group1 = [get_url('https://www.baidu.com') for _ in range(10)]
       group2= [get_url('https://www.baidu.com') for _ in range(10)]
       group1.cancel()  # 将第一组任务取消
       loop.run_until_complete(asyncio.gather(*group1,*group2))
  4. asyncio.gather戻り、未完のタスクの完全な実装は、あなたが上記のようなその動作、中にいくつかの操作を行うことができreturn_whenますが、どの方法タスクの完了から結果を自分たちのニーズを実行したい場合は、結果を返すようにタイミングを制御するために、実際の状況を返します取ります。

  5. 例を取得し、次のような結果を返します。

    tasks = [Task1,Task2,Task3]
    # gather的获取结果方法
    results = await asyncio.gather(*tasks)  # 注意此处gather方法不支持可迭代对象做参数,需要加*脱掉[]
    
    # wait的获取结果方法
    # 第一种
    done,pending = await asyncio.wait(tasks)  # 标准格式的wait方法
    results = [task.result() for task in done]
    # 第二种
    await asyncio.wait(tasks)
    results = [task.result() for task in tasks]

おすすめ

転載: www.cnblogs.com/FuckSpider/p/11584838.html