ホームページ  >  記事  >  バックエンド開発  >  Pythonコルーチンの詳細な説明(例付き)

Pythonコルーチンの詳細な説明(例付き)

不言
不言転載
2018-10-08 16:22:275916ブラウズ

この記事では、Python コルーチンの詳細な説明 (例付き) を提供します。一定の参考価値があります。必要な友人は参照できます。お役に立てれば幸いです。

プロセスとスレッドの切り替えには時間がかかり、スレッドプロセスの現在のステータスが保存され、次回実行を継続します。 CPU をあまり必要としないプログラム、つまり IO 集中型プログラムと比較して、コルーチンはスレッド プロセスよりもリソース消費が少なく、切り替えが速く、IO 集中型プログラムにより適しています。コルーチンもシングルスレッドなのでマルチコア CPU を活用できません。マルチコア CPU を活用したい場合は、プロセス コルーチン メソッドまたはプロセス スレッド コルーチン メソッドを使用できます。

1. コルーチンの簡単な実装

コルーチンの原理は、次のようにジェネレーターを介して実装することです: プログラムは 19 行目まで実行され、コンシューマー関数は 13 行目まで実行され、次のジェネレーター、プロデューサーを実行します 関数は 8 行目で停止し、コンシューマ関数の 13 行目に戻り、実行を継続します。ループは再び 13 行目に到達し、ジェネレーター関数は最後の yield が停止したところから実行されます。このようにしてループが完了すると、同時実行効果が完了します。

しかし、以下の 2 番目の例に示すように、ループ内で 2 つの関数を連続して実行しても結果は同じであると言う人もいるかもしれません。ここで言いたいのは、これは関数の実行位置を保持しているわけではなく、ある関数を実行した後に別の関数に切り替えるだけであり、CPU を待たせる操作があった場合には切り替えることができないということです。 CPU の待機が必要な操作が発生した場合は、積極的に CPU を放棄し、関数の実行位置を記憶し、切り替えて次回実行を継続します。これは同時操作とみなされ、プログラムの同時実行効果が向上します。

コルーチンは単にプロデューサーとコンシューマーを実装するだけです

import time
def producer():
    while True:
        time.sleep(1)
        print("+++++ 1个包子", time.strftime("%X"))
        yield
def consumer():
    while True:
        next(prd)
        print("----- 1个包子", time.strftime("%X"))
if __name__ == "__main__":
    prd = producer()
    consumer()
# 输出结果
+++++ 1个包子 16:22:30
----- 1个包子 16:22:30
+++++ 1个包子 16:22:31
----- 1个包子 16:22:31
+++++ 1个包子 16:22:32
----- 1个包子 16:22:32
+++++ 1个包子 16:22:33
----- 1个包子 16:22:33

シーケンシャル実行効果

import time
def producer():
    time.sleep(1)
    print("+++++ 1个包子", time.strftime("%X"))
def consumer():
    print("----- 1个包子", time.strftime("%X"))
if __name__ == "__main__":
    while True:
        producer()
        consumer()
# 输出结果
+++++ 1个包子 16:22:30
----- 1个包子 16:22:30
+++++ 1个包子 16:22:31
----- 1个包子 16:22:31
+++++ 1个包子 16:22:32
----- 1个包子 16:22:32
+++++ 1个包子 16:22:33
----- 1个包子 16:22:33

2, greenlet

greenlet モジュールpip install greenlet をインストールする必要があります。 Greenlet の原則は、ジェネレーターのカプセル化です。 greenlet クラスはメソッド switch: 切り替えが必要な場合に指定されたコルーチンに切り替えるメソッドを提供します。

from greenlet import greenlet
import time
def producer():
    while True:
        time.sleep(1)
        print("+++++ 1个包子", time.strftime("%X"))
        gr2.switch()  # 切换到gr2运行
def consumer():
    while True:
        print("----- 1个包子", time.strftime("%X"))
        gr1.switch()  # 切换到gr1运行
if __name__ == "__main__":
    gr1 = greenlet(producer)
    gr2 = greenlet(consumer)
    gr1.switch()  # 切换到gr1运行
# 输出结果
+++++ 1个包子 09:39:45
----- 1个包子 09:39:45
+++++ 1个包子 09:39:46
----- 1个包子 09:39:46
+++++ 1个包子 09:39:47
----- 1个包子 09:39:47

3. gevent

gevent モジュールもインストールする必要があります (pip install gevent)。 gevent は gevent を再カプセル化したもので、時間のかかる操作を自動的に識別し、他のコルーチンに切り替えることができます。 gevent は、時間のかかる操作が発生した場合にのみ実行中のコルーチンを切り替え、時間のかかる操作が発生しない場合は積極的に切り替えないことに注意してください。

gevent.spawn(*args, **kwargs) 可変長パラメータの最初のパラメータはコルーチンによって実行されるメソッド fn で、残りは fn のパラメータです。コルーチンを開始したら、join メソッドを呼び出します。

gevent モジュールで時間のかかる操作を特定するには 2 つの方法があります。① gevent モジュールでオーバーライドされたクラスを使用します。例: gevent.socket gevent.sleep ② パッチ適用方法、すべてのコードの前。から gevent import Monkey はこのモジュールをインポートし、monkey.patch_all() はこのメソッドを呼び出します。

記述されたコードを変更する必要がないように、2 番目の方法を使用することをお勧めします

通常の状況では、gevent は時間のかかる操作を認識しません

import time
import gevent
def producer():
    for i in range(3):
        time.sleep(1)
        print("+++++ 1个包子", name, time.strftime("%X"))
def consumer():
    for i in range(3):
        time.sleep(1)
        print("----- 1个包子", name, time.strftime("%X"))
if __name__ == "__main__":
    g1 = gevent.spawn(producer, "zhangsan")
    g2 = gevent.spawn(consumer, "lisi")
    g1.join()
    g2.join()
# 输出结果
+++++ 1个包子 zhangsan 10:42:38
+++++ 1个包子 zhangsan 10:42:39
+++++ 1个包子 zhangsan 10:42:40
----- 1个包子 lisi 10:42:41
----- 1个包子 lisi 10:42:42
----- 1个包子 lisi 10:42:43

gevent は時間のかかる操作方法 1 を特定します。gevent のモジュールを使用します。

import time
import gevent
def producer():
    for i in range(3):
        gevent.sleep(1)
        print("+++++ 1个包子", time.strftime("%X"))
def consumer():
    for i in range(3):
        gevent.sleep(1)
        print("----- 1个包子", time.strftime("%X"))
if __name__ == "__main__":
    g1 = gevent.spawn(producer)
    g2 = gevent.spawn(consumer)
    g1.join()
    g2.join()
# 输出结果
+++++ 1个包子 10:43:04
----- 1个包子 10:43:04
+++++ 1个包子 10:43:05
----- 1个包子 10:43:05
+++++ 1个包子 10:43:06
----- 1个包子 10:43:06

gevent は時間のかかる操作方法 2 を特定します。patch

import time
import gevent
from gevent import monkey
monkey.patch_all()
def producer():
    for i in range(3):
        time.sleep(1)
        print("+++++ 1个包子", time.strftime("%X"))
def consumer():
    for i in range(3):
        time.sleep(1)
        print("----- 1个包子", time.strftime("%X"))
if __name__ == "__main__":
    g1 = gevent.spawn(producer)
    g2 = gevent.spawn(consumer)
    g1.join()
    g2.join()
# 输出结果
+++++ 1个包子 10:44:04
----- 1个包子 10:44:04
+++++ 1个包子 10:44:05
----- 1个包子 10:44:05
+++++ 1个包子 10:44:06
----- 1个包子 10:44:06

開いているコルーチンがたくさんある場合、一つ一つjoinメソッドを呼び出すのは少々面倒なので、geventでは全てのコルーチンを一度に結合できるメソッドjoinall()を提供しています。 joinall() メソッドは、すべてのコルーチンを含むリストを渡します。

joinall

import time
import gevent
from gevent import monkey
monkey.patch_all()
def producer(name):
    for i in range(3):
        time.sleep(1)
        print("+++++ 1个包子", name, time.strftime("%X"))
def consumer(name):
    for i in range(3):
        time.sleep(1)
        print("----- 1个包子",  name, time.strftime("%X"))
if __name__ == "__main__":
    gevent.joinall([gevent.spawn(producer, "zhangsan"), gevent.spawn(consumer, "lisi")])
# 输出结果
+++++ 1个包子 zhangsan 10:51:34
----- 1个包子 lisi 10:51:34
+++++ 1个包子 zhangsan 10:51:35
----- 1个包子 lisi 10:51:35
+++++ 1个包子 zhangsan 10:51:36
----- 1个包子 lisi 10:51:36

4. コルーチン アプリケーション、同時サーバー

サーバーはクライアント メッセージを受信し、そのまま送り返します

server

import socket
import gevent
from gevent import monkey
monkey.patch_all()
def fn(conn):
    msg = conn.recv(1024).decode("utf-8")
    print("服务的收到>>>", msg)
    conn.send(msg.encode("utf-8"))
sk = socket.socket()
sk.bind(("127.0.0.1", 8899))
sk.listen()
while True:
    conn, addr = sk.accept()
    print("已连接服务器-->", addr)
    gevent.spawn(fn, conn)
sk.close()
# 输出结果
已连接服务器--> ('127.0.0.1', 53878)
已连接服务器--> ('127.0.0.1', 53879)
已连接服务器--> ('127.0.0.1', 53880)
服务的收到>>> client1
服务的收到>>> client2
服务的收到>>> client3

クライアント

import socket
sk = socket.socket()
sk.connect(("127.0.0.1", 8899))
msg = input("客户端发送的内容>>> ")
sk.send(msg.encode("utf-8"))
msg = sk.recv(1024).decode("utf-8")
print("客户端收到>>>", msg)
sk.close()
# 输出结果
客户端发送的内容>>> client1
客户端收到>>> client1

以上がPythonコルーチンの詳細な説明(例付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。