コルーチン
マイクロスレッド、ファイバーとしても知られるCoroutine。英語名はCoroutineです。一文はスレッドが何であるかを説明します:coroutineはユーザーモードの軽量スレッドです。
コルーチンには、独自のレジスタコンテキストとスタックがあります。定期的なスケジューリングを切り替えるときは、レジスタコンテキストとスタックを他の場所に保存し、元に戻すときに以前に保存したレジスタコンテキストとスタックを復元します。したがって:
コルーチンは、最後に呼び出されたときの状態(つまり、すべてのローカル状態の特定の組み合わせ)を保持できます。プロセスが再入力されるたびに、最後の呼び出しの状態に入るのと同じです。つまり、最後に呼び出したときのロジックを入力します。ストリームの場所。
コルティンの利点:
短所:
歩留まりを使用して通常の操作を実装する例
import time
import queue
def consumer(name):print("--- starting eating baozi...")while True:
new_baozi =yieldprint("[%s] is eating baozi %s"%(name, new_baozi))
# time.sleep(1)
def producer(): #プロデューサー
r = con.__next__()
r = con2.__next__()
n =0while n <5:
n +=1
con.send(n)
con2.send(n)print("3[32;1m[producer]3[0m is making baozi %s"% n)if __name__ =='__main__':
con =consumer("c1")
con2 =consumer("c2")
p =producer()
プログラム実行の結果は次のとおりです。
— starting eating baozi…
— starting eating baozi…
[ c1] is eating baozi 1
[ c2] is eating baozi 1
[ producer] is making baozi 1
[ c1] is eating baozi 2
[ c2] is eating baozi 2
[ producer] is making baozi 2
[ c1] is eating baozi 3
[ c2] is eating baozi 3
[ producer] is making baozi 3
[ c1] is eating baozi 4
[ c2] is eating baozi 4
[ producer] is making baozi 4
[ c1] is eating baozi 5
[ c2] is eating baozi 5
[ producer] is making baozi 5
問題が発生しています。複数の同時実行の効果を実現できる理由は、各プロデューサーに時間のかかるコードがないため、まったくスタックしないためです。このときにプロデューサーが(1)スリープすると、速度が急激に低下します。遅くなります、次の関数を見てください
def home():
print(“in func 1”)
time.sleep(5)
print(“home exec done”)
def bbs():
print(“in func 2”)
time.sleep(2)
def login():
print(“in func 2”)
nginxが関数によって処理されるたびに要求に来るが、それがシングルスレッドの状況である場合、nginxはバックグラウンド処理でシングルスレッドであるため、nginxがホームページを要求すると、シングルスレッドの同僚の場合は3つの要求が来ます。私は何をすべきか?それは何度も何度も連続実行でなければなりませんが、それが同時であると彼に感じさせるために、私はさまざまなコルティンを切り替える必要がありますが、いつ切り替える必要がありますか?それで、リクエストが来て直接印刷した場合、すぐにこの場所に切り替えますか?詰まりがないため、カードの所有者ではないため、すぐに切り替える必要はありません。たとえば、彼が何かをする必要がある場合、家全体が5秒かかり、シングルスレッドはシリアルです。コルティンを使用しても、シリアルです。同時実行の効果を確保するために、切り替えはいつ実行する必要がありますか? time.sleep(5)はここでbbsリクエストに切り替える必要がありますが、bbsもスリープしている場合はどうなりますか?次に、次のログインに切り替わります。次に、その切り替えだけです。上記のプログラムの同時実行効果を単一のスレッドでどのように達成できますか?つまり、io操作が発生したときに切り替えます。コルーチンが大きな同時実行を処理できる理由は、実際にはio操作を絞り出すためです。つまり、io操作が切り替えられます。つまり、このプログラムはCPUのみが動作しているため、速度が非常に高速です。切り替え後、いつ元に戻しますか?言い換えれば、プログラムはどのようにしてio操作の完了を自動的に監視できますか?次に、次の知識ポイントを見てください!
Greenlet
Greenletは、Cで実装されたcoroutineモジュールです。pythonに付属するyieldと比較すると、この関数を次のように宣言しなくても、任意の関数を自由に切り替えることができるカプセル化されたcoroutineです。発生器。
from greenlet import greenlet
def test1():print(12)
gr2.switch() #gr2に切り替えます
print(34)
gr2.switch() #gr2に切り替えます
def test2():print(56)
gr1.switch() #gr1に切り替えます
print(78)
gr1 =greenlet(test1) #コルティンを開始する
gr2 =greenlet(test2) #
gr1.switch() #gr1に切り替えます
プログラム実行の結果は次のとおりです。
12
56
34
78
Gevent
上のグリーンレットは手動ギアの自動切り替えです。次に自動ギアGeventの自動切り替えを見てみましょう。IOに遭遇すると切り替わります。
Geventは、geventを介して同時同期または非同期プログラミングを簡単に実装できるサードパーティのライブラリです。geventで使用されるメインモードはGreenletです。これは、C拡張モジュールの形式でPythonに接続する軽量のコルーチンです。グリーンレットはすべてメインプログラムのオペレーティングシステムプロセス内で実行されますが、協調してスケジュールされます。
非常にシンプルなコルチンスイッチングアップルレットを見てみましょう
import gevent
def func1():print('3[31;1m LiChuangはHaitaoと関わっています...3[0m')
gevent.sleep(2) #IOを模倣する
print('3[31;1m Li Chuangが戻って、Haitaoとの関わりを続けました...3[0m')
def func2():print('3[32;1m LiChuangがHailongとの契約に切り替えました...3[0m')
gevent.sleep(1)print('3[32;1m Li ChuangがHaitaoを終了し、戻ってきてHailongを続行します...3[0m')
gevent.joinall([
gevent.spawn(func1), #スポーンはcoroutineを開始します
gevent.spawn(func2),])
プログラム実行の結果は次のとおりです。
LiChuangはHaitaoと関わっています...
LiChuangはHailongとの連携に切り替えました...
Li ChuangがHaitaoを終えた後、彼は戻ってきて、Hailongと関わり続けました...
Li Chuangは戻って、Haitaoとの関わりを続けました...
コルティンクローラー
コロチンを使用して単純なクローラーを実装する
from gevent import monkey; monkey.patch_all() #現在のプログラムのすべてのio操作を個別にマークします
import gevent #日常のモジュール
from urllib.request import urlopen #クローラーに必要なモジュール
def f(url):print('GET: %s'% url)
resp =urlopen(url)
data = resp.read()print('%d bytes received from %s.'%(len(data), url))
gevent.joinall([ #ルーチンを使用してWebページを同時にクロールする
gevent.spawn(f,'https://www.python.org/'),
gevent.spawn(f,'https://www.yahoo.com/'),
gevent.spawn(f,'https://github.com/'),])
プログラム実行の結果は次のとおりです。
GET: https://www.python.org/
GET: https://www.yahoo.com/
GET: https://github.com/
59619 bytes received from https://github.com/.
495691 bytes received from https://www.yahoo.com/.
48834 bytes received from https://www.python.org/.
コルチンソケット
geventを介してシングルスレッドでマルチソケット同時実行を実現
# socket_server #
import sys
import socket
import time
import gevent
from gevent import socket,monkey
monkey.patch_all()
def server(port):
s = socket.socket()
s.bind(('HW-20180425SPSL', port))
s.listen(500)while True:
cli, addr = s.accept()
gevent.spawn(handle_request, cli)
def handle_request(conn):try:while True:
data = conn.recv(1024)print("recv:", data)
conn.send(data)if not data:
conn.shutdown(socket.SHUT_WR)
except Exception as ex:print(ex)finally:
conn.close()if __name__ =='__main__':server(8001)
# socket_client #
import socket
HOST ='HW-20180425SPSL' # The remote host
PORT =8001 # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))while True:
msg =bytes(input(" :"),encoding="utf8")
s.sendall(msg)
data = s.recv(1024)
# print(data)print('Received',repr(data))
s.close()
プログラム実行の結果は次のとおりです。
socket_client.py
: lala
Received b’lala’
:
socket_server.py
recv: b’heihei’
**イベント駆動型および非同期IO **
一般に、サーバー処理モデルのプログラムを作成する場合、次のモデルがあります。
(1)要求を受信するたびに、要求を処理するための新しいプロセスが作成されます。
(2)要求を受信するたびに、要求を処理するための新しいスレッドが作成されます。
(3)リクエストを受信するたびに、イベントリストに入れ、メインプロセスにノンブロッキングI / Oを介してリクエストを処理させます
上記の方法には独自のメリットがあります。
(1)の方法では、新しいプロセスを作成するオーバーヘッドが比較的大きいため、サーバーのパフォーマンスが低下しますが、実装は比較的簡単です。
(2)メソッドは、スレッドの同期により、デッドロックやその他の問題に直面する可能性があります。
3番目の方法では、アプリケーションコードを作成するときに、ロジックが前の2つよりも複雑になります。
さまざまな要因を考慮すると、一般的に、方法(3)はほとんどのネットワークサーバーで採用されている方法であると考えられています。
写真を見て、イベント駆動型モデルについて話します
UIプログラミングでは、マウスクリックに応答する必要があることがよくあります。最初にマウスクリックを取得するにはどうすればよいですか。
方法1:スレッドを作成します。スレッドは、マウスクリックがあるかどうかを検出するためにループしています。この方法には、次の欠点があります。
CPUリソースが無駄になります。マウスクリックの頻度は非常に少ないかもしれませんが、スキャンスレッドは引き続き検出をループするため、CPUリソースが大量に浪費されます。マウスクリックをスキャンするためのインターフェイスがブロックされている場合はどうなりますか?
ブロックされていると、次の問題が再び発生します。マウスのクリックをスキャンするだけでなく、キーボードが押されているかどうかもスキャンすると、マウスのスキャン時にマウスがブロックされるため、キーボードがスキャンされない場合があります。
サイクルでスキャンする必要のあるデバイスが多数ある場合、これにより応答時間の問題が発生します。
したがって、この方法は非常に悪いです。
方法2:イベント駆動型モデル
現在、ほとんどのUIプログラミングはイベント駆動型モデルです。たとえば、多くのUIプラットフォームは、マウス押下イベントを表すonClick()イベントを提供します。イベント駆動型モデルの一般的な考え方は次のとおりです:
イベント(メッセージ)キューがあります。
マウスを押すと、クリックイベント(メッセージ)がこのキューに追加されます。
常にキューからイベントを取り出し、onClick()、onKeyDown()などのさまざまなイベントに応じてさまざまな関数を呼び出すループがあります。
イベント(メッセージ)は通常、独自の処理関数ポインターを保存するため、各メッセージには独立した処理関数があります。
**イベント駆動型モデルとは何ですか? ****
**実際、それはイベントに対応することです! ****
イベント駆動型プログラミングはプログラミングパラダイムであり、プログラムの実行フローは外部イベントによって決定されます。これは、イベントループを含むことを特徴とし、外部イベントが発生すると、コールバックメカニズムを使用して対応する処理をトリガーします。他の2つの一般的なプログラミングパラダイムは、(シングルスレッド)同期プログラミングとマルチスレッドプログラミングです。
例を使用して、シングルスレッド、マルチスレッド、およびイベント駆動型のプログラミングモデルを比較対照します。次の図は、これら3つのモードでプログラムによって実行された作業を時間の経過とともに示しています。このプログラムには3つのタスクがあり、各タスクはI / O操作を待機している間それ自体をブロックします。 I / O操作のブロックに費やされた時間は灰色のボックスでマークされています。
シングルスレッド同期モデルでは、タスクは順番に実行されます。 I / Oが原因でタスクがブロックされた場合、他のすべてのタスクは、順番に実行する前に、完了するまで待機する必要があります。この明確な実行シーケンスとシリアル化の動作は簡単に推測できます。タスクが相互に依存していないが、それでも相互に待機する必要がある場合、これにより、実行速度を下げるためにプログラムが不要になります。
マルチスレッドバージョンでは、これら3つのタスクは別々のスレッドで実行されます。これらのスレッドはオペレーティングシステムによって管理され、マルチプロセッサシステムで並列処理することも、シングルプロセッサシステムでインターリーブすることもできます。これにより、特定のリソースでスレッドがブロックされている間、他のスレッドが実行を継続できます。同様の機能を実行する同期プログラムと比較すると、この方法はより効率的ですが、プログラマーは共有リソースを保護し、複数のスレッドが同時にアクセスするのを防ぐためのコードを作成する必要があります。マルチスレッドプログラムは、ロック、再入可能関数、スレッドローカルストレージ、またはその他のメカニズムなどのスレッド同期メカニズムを介してスレッドの安全性の問題に対処する必要があるため、推測がより困難です。不適切な実装は、微妙で望ましくないことにつながります。バグ。
プログラムのイベント駆動型バージョンでは、3つのタスクがインターリーブされますが、それでも単一のスレッドによって制御されます。 I / Oまたはその他のコストのかかる操作を処理する場合は、イベントループへのコールバックを登録し、I / O操作が完了したら実行を続行します。コールバックは、イベントの処理方法を記述します。イベントループはすべてのイベントをポーリングし、イベントが到着したときにイベントの処理を待機しているコールバック関数にそれらを割り当てます。このようにして、追加のスレッドを必要とせずに、プログラムを可能な限り実行することができます。イベント駆動型プログラムは、プログラマーがスレッドの安全性の問題を気にする必要がないため、マルチスレッドプログラムよりも動作を推測するのが簡単です。
次の環境に直面する場合、通常、イベント駆動型モデルが適切な選択です。
1.プログラムには多くのタスクがあり、...
2.タスクは高度に独立しているため(したがって、相互に通信したり、相互に待機したりする必要はありません)、...
3.イベントの到着を待っている間、一部のタスクはブロックされます。
これは、同期の必要がないため、アプリケーションがタスク間で可変データを共有する必要がある場合にも適しています。
ネットワークアプリケーションは通常、これらの特性を備えているため、イベント駆動型プログラミングモデルにうまく適合します。
ここで提起される問題は、上記のイベント駆動型モデルでは、IOに遭遇する限り、イベントを登録し、メインプログラムは他のことを続行できるということです。ioが処理された後でのみ、中断を再開します。タスク、これは本質的にどのように達成されますか?ハハ、一緒にこの謎を解き明かそう。 。 。 。
PythonIOポートの多重化に関するこの記事を詳細に参照してください
上記はPythoncoroutineの詳細な内容です。Pythoncoroutineの詳細については、ZaLou.Cnの他の関連記事に注意してください。