ホームページ  >  記事  >  バックエンド開発  >  Python のコルーチン関数を深く理解する

Python のコルーチン関数を深く理解する

零到壹度
零到壹度オリジナル
2018-04-14 11:16:161736ブラウズ

この記事の内容は、Python のコルーチン関数についての深い理解を共有することです。必要な友人が参照できるようにします。

コンセプト:

によって与えられた定義による。 Wikipedia、「コルーチン」 ルーチンは、非プリエンプティブ マルチタスク用のサブルーチンを生成するコンピューター プログラム コンポーネントです。コルーチンにより、異なるエントリ ポイントが異なる場所でプログラムの実行を一時停止または開始できます。技術的な観点から見ると、「コルーチンは実行を一時停止できる関数」です。 「発電機のようなもの」と理解すれば、あなたの考えは正しいです。

コルーチンは、マイクロスレッドとも呼ばれ、サブルーチンのように見えますが、実行プロセス中に現在のサブルーチンを中断して他のサブルーチンを実行し、その後前のサブルーチンを実行することができます。サブルーチンですが、関連情報は以前と同じです。

コルーチンはスレッドとは異なります。スレッドはプリエンプティブなスケジューリングですが、コルーチンは独自のスケジューリングを行う必要があります。
サブルーチン呼び出しには常に 1 つの入り口と 1 つの戻りがあり、呼び出しシーケンスは明確です。コルーチンの呼び出しはサブルーチンとは異なります。コルーチンもサブプログラムのように見えますが、実行中にサブプログラム内で中断し、他のサブプログラムの実行に切り替えて、適切なタイミングで戻って実行を継続できます。

コルーチンの利点:

  • コルーチンの利点は、実行効率が非常に高いことです。サブルーチンの切り替えはスレッドの切り替えではなく、プログラム自体によって制御されるため、スレッドの切り替えによるオーバーヘッドはありません。マルチスレッドと比較して、スレッドの数が増えるほど、コルーチンのパフォーマンス上の利点がより顕著になります。コルーチンのマルチタスクの実行に非常に適しています。

  • コルーチンにはスレッド セーフティの問題はありません。プロセスには同時に複数のコルーチンを含めることができますが、アクティブになるコルーチンは 1 つだけであり、コルーチンのアクティブ化と休止状態はオペレーティング システムではなくプログラミングを通じてプログラマによって制御されます。

ジェネレーターはコルーチン原理を実装します

例:

def func(n):
    index=0
    if index<=n:
        c=yield 1
        print("task------{}".format(c))
        index+=1f=func(3)
n=next(f)
print(n)try:
    n=f.send(5)#程序就直接结束了
    print("n是{}".format(n))except StopIteration as e:    pass
输出打印:1task------5

説明:

  • 明らかに func はジェネレーターであり、send メソッドにはパラメーターがあり、最後の保留中の yield ステートメントの戻り値を指定します。

  • send は例外を処理する必要があります。

  • 一般に、send メソッドと next メソッドの唯一の違いは、send メソッドの実行時に、最後に保留中の yield ステートメントの戻り値が、ジェネレーター メソッドとの対話を実現するためにパラメーターを通じて最初に設定されることです。ただし、ジェネレータ オブジェクトが次のメソッドを実行する前に、yield ステートメントが中断されていないため、send メソッドの実行時にエラーが報告されることに注意してください。

  • sendメソッドのパラメータがNoneの場合はnextメソッドと全く同等です。

ジェネレーターはプロデューサーとコンシューマーのパターンを実装します:

def cunsumer():
    while True:
        n=yield 3
        if not n:            return
        print(&#39;cunsumer{}&#39;.format(n))def product(c):
    c.send(None)
    n=0
    while n<5:
        n=n+1
        r=c.send(n)
        print("product{}".format(r))
    c.close()
c=cunsumer()
product(c)
打印:
cunsumer1
product3
cunsumer2
product3
cunsumer3
product3
cunsumer4
product3
cunsumer5
product3

説明:

c.send(None) は最初にプロデューサーで実行されます。その目的は、コンシューマーを最初に切断させてから、send を使用することです。値を渡す。最初に 1 が渡されると、コンシューマは 1 を出力し、プロデューサはコンシューマの譲歩後の値である r を出力します。

greenlet の紹介

CPython (標準 Python) はジェネレーターを介してコルーチンを実装できますが、使い方はあまり便利ではありません。

同時に、Python の派生である Stackless Python は、より使いやすいネイティブ コルーチンを実装します。

そこで、みんなでスタックレスのコルーチンコードを取り出して、CPython拡張パッケージに作り始めました。

これが greenlet の起源であるため、greenlet はネイティブ コルーチンを下部に実装する C 拡張ライブラリです。

コード図:

from greenlet import greenletimport randomimport timedef Producer():
    while True:
        item = random.randint(0,10)
        print("生产了{}".format(item))
        c.switch(item)#切换到消费者,并将item传入消费者
        time.sleep(1)def consumer():
    print(&#39;我先执行&#39;)    #p.switch()
    while True:
        item = p.switch()#切换到生产者,并且等待生产者传入item
        print(&#39;消费了{}&#39;.format(item))
c = greenlet(consumer)#将一个普通函数变成一个协程p = greenlet(Producer)
c.switch()#让消费者先进入暂停状态(只有恢复了才能接收数据)

greenletの値:

  • 高性能ネイティブコルーチン

  • より明確なセマンティクスによる明示的な切り替え

  • 関数をコルーチンに直接ラップ元のコードスタイルを維持する

gevent coroutine

epoll に基づいたコールバック プログラミング モデルがありますが、使用するのが難しいです。

たとえジェネレーター コルーチンを使用して複雑なカプセル化を実行し、プログラミングの難しさを単純化することができたとしても。
しかし、まだ大きな問題があります: カプセル化は難しく、2 つのライブラリ libev (epoll に基づく) と greenlet をカプセル化することで、既存のコードをほぼ完全に書き直す必要があります

gevent。
カプセル化してスレッドのような方法でコルーチンを使用できるようにしてください。

元のコードを書き直すことなく、epoll とコルーチンの機能を最大限に活用できるようになりました。

コード図:

from gevent import monkey;monkey.patch_all()#会把python标准库当中一些阻塞操作变成非阻塞import geventdef test1():
    print("11")
    gevent.sleep(4)#模拟爬虫请求阻塞
    print("33")def test2():
    print("22")
    gevent.sleep(4)
    print("44")
gevent.joinall([gevent.spawn(test1),gevent.spawn(test2)])#joinall 阻塞当前协程,执行给定的greenlet#spawn 启动协程,参数就是函数的名字

gevent 値:

障害が発生した場合は、別のコルーチンに切り替えて実行を続行します。

  • ブロックを回避するには、epoll ベースの libev を使用してください。

  • gevent に基づいた効率的なコルーチンを使用して実行を切り替えます。

  • 障害が発生した場合にのみ切り替えます。ラウンドロビン オーバーヘッドやスレッド オーバーヘッドはありません。

geventは並行サーバーを実装します

from gevent import monkey;monkey.patch_all()  #建议放在首行,会把python标准库当中一些阻塞操作变成非阻塞import geventimport socket


server=socket.socket()
server.bind((&#39;&#39;,6666))
server.listen(5)
print("开始监听")def readable(con,addr):
    print("客户端{}接入".format(addr))    while True:
        data=con.recv(1024)        if data:
            print(data)        else:
            con.close()            breakwhile True:
    con,addr=server.accept()
    gevent.spawn(readable,con,addr)#将readable函数变为协程,并且把con和addr传入其中。

geventコルーチン通信

geventにも独自のキューがあります。使い方は基本的にはスレッドと同じです。

gevent と queue に基づくプロデューサーとコンシューマーのパターン

from gevent import monkey;monkey.patch_all()import geventfrom gevent.queue import Queueimport randomdef producter(queue):
    while True:
        item=random.randint(0,99)
        print(&#39;生产了{}&#39;.format(item))
        queue.put(item)
        gevent.sleep(1)def comuser(queue):
    while True:
        item=queue.get()
        print(&#39;消费了{}&#39;.format(item))
queue=Queue()
p=gevent.spawn(producter,queue)
c=gevent.spawn(comuser,queue)
gevent.joinall([p,c])
打印:
生产了33消费了33生产了95消费了95生产了92消费了92...


相关推荐:

python中多进程+协程的使用

python中协程

Python 协程的详细用法和例子

python 协程示例

以上がPython のコルーチン関数を深く理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。