なぜPython協業の使い方がまだ分かりませんか?

6660 ワード

私は思想のあるプログラム猿について、一生実践者を勉強します。現在は創業チームでteam leadを担当しています。技術スタックはAndroid、Python、JavaとGoに関連しています。これも私達のチームの主要な技術スタックです。Github:https://github.com/hylinux1024 WeChat公衆号:生涯開発者(angrycode)
前の編の「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+バージョンにPythonaysncのキーワードが追加されました。この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.Futureasyncio.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参照
  • https://docs.python.org/3/library/asyncio-task.html