Pythonマルチスレッドの深い理解

Pythonのマルチスレッドは偽のマルチスレッドです。コアがいくつあっても、一度に操作できるコアは1つだけです。 Pythonのマルチスレッドを利用し、CPUコンテキストの切り替えを利用するだけでは、同時実行のように見えますが、実際には単一のスレッドであるため、偽の単一のスレッドです。

**では、いつマルチスレッドを使用しますか? ****

最初に知っておくべきこと:

**Pythonのマルチスレッドは、CPUを集中的に使用する操作には適していませんが、SocketServer **などのioを集中的に使用するタスクには適していません。

**CPUを集中的に使用するタスクが増えた場合はどうなりますか? ****

まず、マルチプロセスのプロセスは独立していますが、pythonのスレッドはシステムのネイティブスレッドを使用し、pythonのプロセスもシステムのネイティブプロセスを使用することに注意してください。ネイティブプロセスはオペレーティングシステムによって維持されます。率直に言って、 Pythonは、Cネイティブコードライブラリのインターフェイスを使用してプロセスを開始します。実際のプロセス管理は引き続きオペレーティングシステムによって行われます。オペレーティングシステム自体にGILグローバルインタープリターロックがありますか?答えはノーで、2つのプロセスのいずれかです。相互のデータは完全に独立しており、相互にアクセスできないため、ロックの概念は不要であり、GILの概念はありません。したがって、この場合、各プロセスには少なくとも1つのスレッドがあります。オペレーティングシステムが8コアの場合はい、8つのプロセスを開始し、各プロセスにスレッドがあります。これは、8つのスレッド、8つのコアで実行される8つのスレッドに相当し、複数のコアを使用することと同等です。これで問題は解決します。

唯一の欠点は、8つのスレッド間のデータを独立して共有できないことです。この方法を使用すると、マルチコアコンピューティングの問題が危険にさらされる可能性があります。

まず、単純なマルチプロセスプログラムを見てください。

import multiprocessing
import time

def run(name):
 time.sleep(2)print('hello', name)if __name__ =='__main__':for i inrange(10):
 p = multiprocessing.Process(target=run, args=('bob%s'%i,))
 p.start()

プログラムの実行結果は次のとおりです。

hello bob0
hello bob1
hello bob3
hello bob2
hello bob5
hello bob9
hello bob7
hello bob8
hello bob4
hello bob6

**したがって、プロセスIDを取得する場合、どのように取得すればよいですか? ****

from multiprocessing import Process
import os

def info(title):print(title)print('module name:', __name__)print('parent process:', os.getppid()) #親プロセスID
 print('process id:', os.getpid()) #自身のプロセスのID
 print("\n\n")

def f(name):info('3[31;1mfunction f3[0m')print('hello', name)if __name__ =='__main__':info('3[32;1mmain process line3[0m')
 p =Process(target=f, args=('bob',))
 p.start()
 p.join()

プログラム実行の結果は次のとおりです。

main process line
module name: main
parent process: 5252
process id: 6576
function f
module name: mp_main
parent process: 6576
process id: 2232
hello bob

実際、この図は、すべての子プロセスが親プロセスによって開始されることを意味します。

プロセス間通信

2つのプロセス間のメモリは互いに独立していると言いますが、2つのプロセスは通信できますか?プロセスAがプロセスBのデータにアクセスするとしますが、アクセスできますか?絶対にアクセスできません!ただし、アクセスしたいだけです。つまり、2つの独立したメモリが相互にアクセスしたいので、どうすればよいですか。

方法はたくさんありますが、そうです!常に、ミドルウェアを見つける必要があることを意味します。ミドルウェアには非常に多くの種類があるので、最初にどれを見てみましょう。

キューの最初のタイプ

使用法は、スレッド化のキューに似ています

from multiprocessing import Process, Queue

def f(q):
 q.put([42, None,'hello'])if __name__ =='__main__':
 q =Queue()
 p =Process(target=f, args=(q,))
 p.start()print(q.get()) # prints "[42, None, 'hello']"
 p.join()

これら2つのプロセスを見てみましょう。親プロセスのqはどのようにして子プロセスに渡されますか?話し合いましょう

ここで、データが共有され、2つのプロセスがqを共有すると思いますか?実際にはそうではありません。これは、qのクローンを作成し、親プロセスで子プロセスを作成することと同じです。つまり、親プロセスが独自のqをクローンします。コピーは子プロセスに渡され、子プロセスはこの時点でデータのコピーをこのqに入れ、親プロセスはそれを取得できます。次に、それを言うのは正しくありません。次に、qのクローンを作成します。つまり、2つのqを作成し、Bがデータをqに配置します。その後、別のqとは関係ありません。つまり、Aのq、ああ、これであるはずです。のように見えますが、実際には、データを共有したいですか?これは、Aとqのデータをシリアル化し、中間位置にシリアル化することと同じであり、中間位置に変換があります。このデータはAに逆シリアル化され、Aのqに配置されます。その後、いわゆるデータ共有が実現されます。

プログラム実行の結果は次のとおりです。

[42, None, ‘hello’]

2番目の種類のパイプ

Pipe()関数は、パイプで接続された接続オブジェクトを返します。これは、デフォルトでは二重(双方向)です。例えば:

from multiprocessing import Process, Pipe

def f(conn):
 conn.send("父よ、お元気ですか?") #息子が送った
 print("son receive:",conn.recv())
 conn.close()if __name__ =='__main__':
 parent_conn, child_conn =Pipe()
 p =Process(target=f, args=(child_conn,))
 p.start()print("father receive:",parent_conn.recv()) #父は受け取る
 parent_conn.send("息子さん、お元気ですか?")
 p.join()

プログラム実行の結果は次のとおりです。

父が受け取る:父、お元気ですか?
息子が受け取る:息子、お元気ですか?

Pipe()によって返される2つの接続オブジェクトは、パイプの両端を表します。各接続オブジェクトには、send()メソッドとrecv()メソッド(およびその他のメソッド)があります。 2つのプロセス(またはスレッド)がパイプの同じ端に対して同時に読み取りまたは書き込みを行おうとすると、パイプ内のデータが破損する可能性があることに注意してください。もちろん、パイプの異なる端を同時に使用する過程で損傷のリスクはありません。

3番目のタイプのマネージャー

Manager()によって返されるmanagerオブジェクトは、Pythonオブジェクトを保存し、他のプロセスがエージェントを使用してそれらを操作できるようにするサーバープロセスを制御します。

Manager()によって返されるマネージャーは、タイプリスト、dict、Namespace、Lock、RLock、Semaphore、BoundedSemaphore、Condition、Event、Barrier、Queue、Value、およびArrayをサポートします。例えば、

from multiprocessing import Process, Manager
import os

def f(d, l):
 d[1]='1'
 d['2']=2
 d[0.25]= None
 l.append(os.getpid())print(l)if __name__ =='__main__':withManager()as manager:
 d = manager.dict() #特別な文法を使用して、複数のプロセス間で受け渡しおよび共有できる辞書を生成します

 l = manager.list(range(5)) # #特別な構文を使用して、複数のプロセス間で受け渡しおよび共有できるリストを生成します。デフォルトには5つのデータがあります
 p_list =[]for i inrange(10):
 p =Process(target=f, args=(d, l))
 p.start()
 p_list.append(p)for res in p_list:
 res.join()print(d)print(l)

プログラム実行の結果は次のとおりです。

[0, 1, 2, 3, 4, 2100]
[0, 1, 2, 3, 4, 2100, 7632]
[0, 1, 2, 3, 4, 2100, 7632, 5788]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072, 7540]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072, 7540, 3904]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072, 7540, 3904, 7888]
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072, 7540, 3904, 7888, 7612]
{1: ‘1’, ‘2’: 2, 0.25: None}
[0, 1, 2, 3, 4, 2100, 7632, 5788, 6340, 5760, 7072, 7540, 3904, 7888, 7612]

プロセスロックとプロセスプール

プロセスロック

プロセスにもロックがあります、何ですか?プロセスはすべて独立していませんか?同じデータを同時に変更する必要はありませんが、どうすればロックできますか?

外観を見てみましょう。スレッドとほぼ同じです。

from multiprocessing import Process, Lock

def f(l, i):
 l.acquire()try:print('hello world', i)finally:
 l.release()if __name__ =='__main__':
 lock =Lock()for num inrange(10):Process(target=f, args=(lock, num)).start()

プログラム実行の結果は次のとおりです。

hello world 3
hello world 1
hello world 2
hello world 5
hello world 7
hello world 4
hello world 0
hello world 6
hello world 8
hello world 9

では、このロックは何をするのでしょうか?

実際には、画面に印刷されている情報が混乱しないようにする機能です!

プロセスプール

上記のプログラムでは、プロセスの開始は親プロセスのメモリデータのクローンであるため、100プロセスの開始は遅くなります。親プロセスがメモリ空間のGを占める場合、100プロセスを開始します。 101Gに相当します。この場合、オーバーヘッドが非常に大きくなります。プロセスを開始して家のクローンを作成するようなもので、しばらくするとHarbinがいっぱいになるため、オーバーヘッドが特に大きくなります。咵嚓を避けるために。多くのプロセスはシステムをダウンさせるため、プロセスプールには制限があります。

プロセスプールは、CPU上で同時に実行されているプロセスの数です。

プロセスプールには2つの方法があります。

from multiprocessing import Process,Pool,freeze_support
import time
import os

def Foo(i):
 time.sleep(2)print("in process",os.getpid())return i+100

def Bar(arg):print('-- exec done:',arg)if __name__ =='__main__':freeze_support()
 pool =Pool(5) #5つのプロセスを同時にプロセスプールに配置できるようにします

 for i inrange(10):
 # pool.apply_async(func=Foo, args=(i,),callback=Bar) #折り返し電話
 pool.apply(func=Foo, args=(i,)) #シリアル
 # pool.apply_async(func=Foo, args=(i,)) #平行

 print('end')

 pool.close()
 pool.join() #プロセスプール内のプロセスは、実行が完了した後に閉じられます。コメントすると、プログラムは直接閉じられます。

プログラムの実行結果は次のとおりです。

in process 7824
in process 6540
in process 7724
in process 8924
in process 9108
in process 7824
in process 6540

ナレッジポイントの拡張:

__ name__ == '__ main__'の機能は次のとおりです。

このコードに関するプログラムを手動で実行すると、その下のプログラムが実行されます。このコードを呼び出すプログラムの場合、その下のプログラムは実行されません。

上記は、Pythonマルチスレッディングの詳細な理解です。Pythonマルチスレッディングの詳細については、ZaLou.Cnの他の関連記事に注意してください。

Recommended Posts

Pythonマルチスレッドの深い理解
Pythonマルチスレッド
Pythonマルチスレッド
Python変数スコープの詳細な理解
pythonでのrbの意味を理解する
Python構文の基本
Pythonの基本構文
Pythonの基礎知識(1)
pythonのPrettytableモジュール
09.Python3の共通モジュール
Pythonの基盤を統合する(4)
Python(7)の基盤を統合する
Pythonのタプルの添え字
wavファイルのPython分析
PythonクローラーのJSの分析
栄光のパイソンキング壁紙
Python(5)の基盤を統合する
gomokuプログラムのPython実装
Pythonサンドボックスエスケープの分析
Python3.10のいくつかの新機能
Pythonオブジェクト指向プログラミングの分析
OpenCVインストールのPythonバージョン
Pythonの9つの機能エンジニアリング手法
python描画モジュールのmatplotlib
パラメータを渡すPythonメソッド
Pythonの基盤を統合する(3)
Python共通モジュールのコレクション
pythonでのwheelの使用法
Pythonの対数法の要約
pythonバックトラッキングテンプレートの詳細な説明
Pythonイールドの使用例の分析
Python開発でのパンダの使用
pythonシーケンスタイプの詳細な説明
pythonリストの逆トラバーサルの実装
魔法の方法とPythonの使用
word2vec操作のPython予備実装
Pythonのディープコピーは完璧ではありません
Pythonは中国語の4つの車輪を処理します
情報エントロピーの例のPython計算
pythonselenium操作cookieの実装
Python開発でのnumpyの使用
地主取引のPythonシミュレーション
Pythonの用途は何ですか
Pythonクローラーのスクレイピーシミュレーションログイン
Python条件付き制御ステートメントの分析
Python1のブラックハットプログラミングアプリケーション
Pythonでの辞書の詳細な使用法
pythonでのosパッケージの使用
Python関数のいくつかの一般的なモード
200のPython標準ライブラリの要約!
pythonクローラーのMongodbとpythonの相互作用
python3登録グローバルホットキーの実装
python学生管理システムの実装
pythonにはいくつかのバージョンがあります
python定義クラスの簡単な使用法
プライベート属性を呼び出すpythonのメソッドの概要
python操作redisのいくつかの例
Pythonモジュールの知識の完全な分析
pythonアクセスAlipayの操作例
Python文字列プーリングの前提
Python3.8の新機能の秘密