プロセスとは何ですか?
プログラムは単独では実行できません。プログラムがメモリにロードされ、システムがリソースを割り当てた場合にのみ実行できます。この実行されたプログラムはプロセスと呼ばれます。プログラムとプロセスの違いは、プログラムは命令の集合であり、プロセスの静的な記述テキストであるのに対し、プロセスはプログラムの実行アクティビティであり、動的な概念であることです。
スレッドとは何ですか?
スレッドは、オペレーティングシステムが操作のスケジューリングを実行できる最小単位です。これはプロセスに含まれており、プロセス内の実際の操作単位となります。スレッドは、プロセス内の単一の順次制御フローを指します。プロセス内では複数のスレッドを同時に実行でき、各スレッドは異なるタスクを並行して実行します。
プロセスとスレッドの違いは何ですか?
スレッドはメモリ空間を共有し、プロセスのメモリは独立しています。
同じプロセスのスレッドは相互に直接通信できますが、2 つのプロセスは中間エージェントを介して相互に通信する必要があります。
新しいスレッドの作成は非常に簡単です。新しいプロセスを作成するには、その親プロセスを複製する必要があります。
スレッドは同じプロセス内の他のスレッドを制御および操作できますが、プロセスは子プロセスのみを操作できます。
Python GIL (グローバル インタプリタ ロック)
開いているスレッドの数や CPU の数に関係なく、Python は実行時に同時に 1 つのスレッドしか許可しません。
Pythonスレッドモジュール
直接呼び出し
インポートスレッド、時間
def run_num(num):
」 ""
定義スレッドによって実行される関数
:param num:
:return:
"""
-
print("実行中番号:% s"%num)
time.sleep(3)
if __name__ == '__main__':
-
# 生成するスレッドインスタンス t1
t1 = threading.Thread(target=run_num,args=(1,))
# スレッドインスタンスを生成します t2
t2 = threading.Th read(target=run_num ,args=(2,))
# スレッドを開始 t1
t1.start()
-
# スレッドを開始t2
t2.start()
# スレッド名を取得します
出力:
番号: 1 で実行中
番号: 2 で実行中-
インポートスレッド、時間
-
)
self. num = num
# 各スレッドで実行する関数を定義します。関数名は run である必要があります
def run (自分):
番号: %s"%self.num)
time.sleep(3)
場合 __name__ == '__main__':
t1 = MyThread (1)
t2 = MyThread(2)
t1.start()
-
t2 .start()
出力:
番号:1で実行中
番号:2で実行中
- 結合とデーモン
参加
の機能結合はメインプロセスをブロックするものであり、結合後のプログラムは実行できません。
- マルチスレッドおよび複数の結合の場合、各スレッドの結合メソッドが順番に実行され、後続のスレッドが実行される前に前のスレッドの実行が完了します。
パラメータがない場合は、スレッドの終了を待ってから後続のプログラムを実行します。
パラメータを設定した後、スレッドが終了するかどうかに関係なく、スレッドで設定された時間待ってから、後続のメイン処理を実行します。
インポートスレッド、時間
class
MyThread(threading.Thread):
self,num):
.Thread.__init__ (自分)
self.num = num
(自分) :
print("スレッド:% s "%self.num)
if __name__ == '__main__':
t1 = MyThread(1)
-
t2 = 私のスレッド( 2 )
t1.start()
t1.join()
t2.start()
t2.join()
番号: 1で実行中
スレッド:1番号:2 で実行中
t1 = MyThread(1)
t2 = MyThread(2)
t1.start()
t1.join(2 )
-
t2.start( )
t2.join()
出力:
番号:1
で走っています番号:2
スレッド: 1
スレッド:2
デーモン デフォルトでは、メインスレッドは終了時にすべての子スレッドの終了を待ちます。メインスレッドが子スレッドを待たずに、終了時にすべての子スレッドを自動的に終了するようにしたい場合は、子スレッドをバックグラウンドスレッド (デーモン) として設定する必要があります。方法はスレッドクラスのsetDaemon()メソッドを呼び出すことです。
インポート時間、スレッド
- def run(n):
- print("
%s".center(20,") *
") %n)
time.sleep(2)
-
print(" done- ".center(20,"
*
"))
-
-
def main():
for- i
in range(5):
-
t = threading.Thread(target=run,args=(i,))
t.start()
- t.join(1)
- print("
開始スレッド",t.getName())
m = threading.Thread(target=main,args=())
# メインスレッドをプログラムのメインスレッドのデーモンスレッドとして機能するデーモンスレッドに設定します。メインスレッドが終了すると、m によって開始された他のサブスレッドも、実行が完了したかどうかに関係なく終了します
m.setDaemon(True)
。
m.start()m.join(3)
print("メインスレッド完了".center(20,"*"))
出力:
-
*********0**********
開始スレッド Thread-2
************ 1*** ******
**********完了************
スレッド開始 Thread-3
**** *****2**********
**メインスレッドが完了しました**
スレッドロック(ミューテックス)
1 つのプロセスが複数のスレッドを開始でき、複数のスレッドが親プロセスのメモリ空間を共有します。つまり、現時点では、2 つのスレッドが同じデータを同時に変更したい場合は、各スレッドが同じデータにアクセスできます。ロックが必要です。
インポート時間、スレッド
def addNum():
-
# 各スレッドでこのグローバル変数を取得します
グローバル番号
-
num -= 1
- # 共有変数を設定します
- num = 100
- thread_list = []
のため i 範囲 (100):
t = threading.Thread(target=addNum)
t.start()
(て)
# すべてのスレッドの実行が完了するまで待ちます
- for
t in thread_list:
t.join()
-
印刷("final num:" ,num)
ロックされたバージョン
- Lock は、他のスレッドが共有リソースにアクセスすることをブロックし、同じスレッドが複数回取得した場合、デッドロックが発生し、プログラムの実行を続行できません。
インポート時間、スレッド
def addNum():
# 各スレッドでこのグローバル変数を取得します
-
グローバル番号
print("- --get num:
",num)
time.sleep(1)
# データを変更する前にロックします
lock.acquire()
# このパブリック変数に対して -1 操作を実行します
-
番号 -= 1
# 変更後のリリース
lock.release()
# 共有変数を設定します
番号 = 100
-
thread_list = [ ]
# グローバルロックを生成
lock = threading.Lock()
for i 範囲内(100):
t = threading.Thread(target=addNum)
t.start()
thread_list.append(t)
-
# すべてのスレッドが完了するまで待ちます完了
-
forin thread_list:
- t.join()
- print("
最終番号:",num)
GIL VS Lock
GIL は、同時に 1 つのスレッドのみが実行できることを保証します。ロックはユーザーレベルのロックであり、GIL とは関係ありません。
RLock (再帰的ロック)
Rlock は、同じスレッド内で複数回取得できるようにします。スレッドによる共有リソースの解放には、すべてのロックの解放が必要です。つまり、n 回の取得には n 回のリリースが必要になります。
- def run1():
print("- 最初の部分データを取得
")
- lock
.acquire()
グローバル番号-
num += 1
- lock
.release()
- return
num
def run2():
print("- 後半データを取得
")
- lock
.acquire()
global num2-
num2 += 1
- lock
.release()
- return
num2
def run3():
lock.acquire()
res = run1()
print("run1 とrun2".center(50,"*"))
res2 = run2()
lock.release()
print(res,res2 )
if __name__ == '__main__':
num,num2 = 0,0
lock = threading.RLock()
fori範囲(10):
t = threading.Thread(target=run3)
t.start()
while threading.active_count() != 1:
print(threading.active_count())
else:
print("すべてのスレッドが完了しました".center(50,"*"))
print(num,num2)
この二つの锁の主要区は、RLock允许が同一回線中に存在しますただし、ロックではこのような状況は許可されません。RLock を使用する場合は、取得と解放を行う必要があります。つまり、n 回取得し、実際に使用されているロックを解放するには、n 回の解放を行う必要があります。 Semaphore(信号量)
相互接続は同時に1つの回線変更データを許可しますが、Semaphoreは同時に一定数の回線変更データを許可します。
インポートスレッド、時間
def run(n):
セマフォ.acquire( )-
time.sleep(1)
-
print(" スレッドを実行します:%s- "%n)
semaphore( )
if- __name__ == '__main__':
# 最多允许5个線程同時進行
-
セマフォre = threading.BoundedSemaphore(5)
-
for- 私は
範囲内(20):
t = threading.Thread(target=run,args=(i,))
t.start()
while _count() != 1:
# print(threading.active_count())
# pass
else:
print("すべてのスレッドが完了しました ".center(50,"*"))
Timer (タイマー)タイマーは一定の間隔で関数を呼び出します。一定の間隔で関数を呼び出したい場合は、次の関数を呼び出す必要がありますタイマー機能でタイマーを再設定します。 Timer は Thread の派生クラスです。
インポートスレッド
def hello():
print("hello, world!")
# 5 秒後の遅延hello関数を実行
t = threading.Timer(5,hello)
t.start()
EventPythonはスレッド間通信用のEventオブジェクトを提供します。これはスレッドによって設定されるシグナル フラグです。シグナル フラグ ビットが false の場合、スレッドは他のスレッドによって命令シグナルが true に設定されるのを待ちます。 Event オブジェクトは、スレッド間の通信を実現するための、シグナルの設定、シグナルのクリア、待機などの単純なスレッド通信メカニズムを実装します。
- シグナルを設定する
Event の set() メソッドを使用して、Event オブジェクト内のシグナル フラグを true に設定します。 Event オブジェクトは、内部信号フラグの状態を決定するための isSet() メソッドを提供します。イベント オブジェクトの set() メソッドが使用されると、isSet() メソッドは true を返します。
- シグナルをクリアする
Event オブジェクト内のシグナルフラグをクリアするには、Event の clear() メソッドを使用します。つまり、Event の clear() メソッドを使用すると、isSet が false に設定されます。 () メソッドは false を返します。
- Waiting
Event の wait() メソッドは、内部シグナルが true の場合にのみ実行され、すぐに戻りを完了します。 Event オブジェクトの内部シグナル フラグが false の場合、wait() メソッドはそれが true になるまで待機してから戻ります。 イベントを使用して、2 つ以上のスレッド間の対話を実現します。信号機を例に挙げます。つまり、信号機として機能するスレッドを開始し、車両として機能する複数のスレッドを生成すると、車両はそれに応じて走行します。赤の停止と緑のルールに従う。
インポートスレッド、時間、ランダム
def light():
イベントではない場合。 isSet():
- if
count print ("
elif カウント
print(" 33[43;1m--黄色信号点灯-- 33[0m".center(50,"*"))
エリフ数 < ; 13:
ifevent.isSet():
event.clear()
print(" 33[41;1m--redライトオン -- 33[0m".center(50,"*"))
else:
count = 0
イベント。 set()
time.sleep(1)
count += 1
-
def car(n):
while 1:
time.sleep(random.randrange(10))
if event.isSet():
print( 「car%sが実行されています...」 n)
if- __name__ == "
__main__":
-
event
= threading.Event() ライト = スレッド。スレッド(target=light,)
- Light.start()
-
for
i in- range(3):
- t = threading.Thread (target=car,args=(i,))
t.start()キュー queue
Python のキューは、スレッド間でデータを交換する最も一般的に使用される形式です。 Queueモジュールはキュー操作を提供するモジュールです。
キューオブジェクトを作成する
インポートキュー
q = queue.Queue(maxsize = 10)
queueクラスはキューです同期を実現します。キューの長さは無制限にすることも、制限することもできます。キューの長さは、Queue コンストラクターのオプションのパラメーター maxsize を通じて設定できます。 maxsize が 1 未満の場合、キューの長さは無制限になります。
キューに値を入れる
q.put("a")
キューオブジェクトの put() メソッドを呼び出して、キューの最後に項目を挿入します。 put() には 2 つのパラメータがあります。最初の項目は必須で、挿入された項目の値です。2 番目のブロックはオプションのパラメータで、デフォルトは 1 です。キューが現在空でブロックが 1 の場合、put() メソッドにより、データ ユニットが解放されるまで呼び出しスレッドが一時停止します。 block が 0 の場合、put() メソッドは完全な例外をスローします。
キューから値を削除する
q.get()
キューオブジェクトのget()メソッドを呼び出して、キューの先頭から項目を削除して返します。オプションのパラメータは block で、デフォルトは True です。キューが空で、ブロックが True の場合、get() により、項目が使用可能になるまで呼び出しスレッドが一時停止します。キューが空でブロックが False の場合、キューは Empty 例外をスローします。
Python Queue モジュールには 3 つのキューとコンストラクターがあります
# 先入れ先出し
class queue.Queue(maxsize=0)
# 先入れ後出し
class queue.LifoQueue(maxsize=0)
# 優先キューレベルが低いほど、最初に出力されます
class queue.Pri orityQueue(最大サイズ=0)
共通メソッド
q = queue.Queue()
# キューのサイズを返す
q.qsize()
-
# キューが空の場合は True を返し、それ以外の場合は False を返します
q.empty()
# キューが空の場合は True を返し、それ以外の場合は False を返します
q .full()
# キューを取得、タイムアウト待ち時間
q.get([block[,timeout]])
# q.と同等get(False)
q.get_nowait()
# 他の操作を実行する前にキューが空になるまで待ちます
q.join()
プロデューサー/コンシューマー モデル
開発とプログラミングでプロデューサーとコンシューマーのパターンを使用すると、ほとんどの同時実行の問題を解決できます。このモードは、プロダクション スレッドとコンシューマ スレッドの作業能力のバランスをとることにより、プログラムの全体的なデータ処理速度を向上させます。
プロデューサーとコンシューマー モデルを使用する理由
スレッドの世界では、プロデューサーはデータを生成するスレッドであり、コンシューマーはデータを消費するスレッドです。マルチスレッド開発では、プロデューサーの処理速度が非常に速く、コンシューマーの処理速度が非常に遅い場合、プロデューサーは、データの生成を続ける前にコンシューマーの処理が完了するのを待つ必要があります。同様に、コンシューマの処理能力がプロデューサの処理能力よりも優れている場合、コンシューマはプロデューサを待つ必要があります。この問題を解決するために、プロデューサー モデルとコンシューマー モデルが導入されました。
プロデューサー-コンシューマー パターンとは何ですか?
プロデューサー-コンシューマー パターンは、コンテナを使用してプロデューサーとコンシューマーの間の強い結合の問題を解決します。プロデューサーとコンシューマーは相互に直接通信するのではなく、ブロッキング キューを介して通信します。そのため、プロデューサーはデータを生成した後、コンシューマーがデータを処理するのを待つことなく、それをブロッキング キューに直接スローしません。プロデューサーにデータを要求しますが、ブロッキング キューから直接データを取得します。ブロッキング キューはバッファーに相当し、プロデューサーとコンシューマーの処理能力のバランスをとります。
生産者-消費者モデルの最も基本的な例。
インポートキュー、スレッド、時間
q = queue.Queue(maxsize=10)
defプロデューサー():
-
カウント = 1
while True:
q.put("bone %s"%count)
print(" 生成された骨 " ,count)
count += 1
def Consumer(name):
-
while q.qsize() > 0:
印刷("%[%s] は [%s] を取得して食べます... "%(name, q.- get
())) time.sleep ( 1)
p = threading.Thread(target=生産者,)
-
c1 = threading.Thread(target=消費者,args=("
豊かな富 ",) )-
c2 = threading.Thread(target=Consumer,args=("
来福 ",))
p.start()
c1.start( )
c2.start()
以上がPython開発 - プロセス、スレッド、コルーチンの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。