コンカレントコンピュータが同時に異なるたくさんのことをやっているようです。プログラムの方法のこの互い違いの実装は、我々はこれらのプログラムを同時に実行することができると思います誤った印象を作成しました。
並列計算機は確かに同時に異なることがたくさんあります。コンピュータは、同時に複数のプログラムを実行可能な複数のCPUコアを含みます。各プログラム命令は、上記コアCPUのそれぞれに個別に実行され、これらのプログラムは、同時に前方に移動することができるようになります。
同一の内部手続きでは、同時実行は、プログラマがより簡単に問題の特定の種類を解決することを可能にするツールです。
並列と並行嘘の主な違いは、スピードができません。
Python言語で書かれた並行プログラムは、比較的容易です。システムコール、子プロセスとC言語の拡張や他のメカニズムも、パイソンと並行していくつかの問題に対処することができます。
しかし、真に並列に実行するために、同時のpythonコードを作るために、それは非常に困難です。
したがって、我々は理解しておく必要がありますどのようにこのような状況の微妙な違いができ、パイソンの特徴を最も適切な使用が提供しています。
子プロセスを管理するためのサブプロセスモジュールと第36条
Pythonは非常によく一緒に接着等のコマンドラインユーティリティツールとすることができるのpythonを行い、子プロセスを、実行し、管理するために、いくつかの非常に強力なライブラリを提供します。
開始サブプロセスのpythonの複数の、それは、私たちは、プログラムの処理能力を高めるために、可能な限り、Pythonでコンピュータプログラム内のすべてのCPUコアを利用することを可能にする、並列に動作することができます。
サブプロセスモジュールと子プロセスを実行して、比較的簡単です。
Shabiウィンドウには、それを忘れて、プログラムのこのセクションを実行することはできません。
ハイライト:
1.サブプロセスモジュールは、サブプロセスを実行し、入力ストリームと出力ストリームを管理することができます。
開発者は、CPUの処理能力を最大限に活用することを可能にする並列スライバに複数のプロセスを実行する2.pythonインタプリタ。
3.タイムアウトパラメータは、デッドロックまたは応答しない子プロセスを回避するための方法を伝えるために渡すことができます。
第37条には、ブロッキングIOをスレッドで実行することができますが、並列計算を行うためにそれを使用しないでください
標準のPython実装はCPythonのと呼ばれています。CPythonの2つのステップがPythonプログラムを実行します。
まず、テキストが解析され、バイトコードにソースコードをコンパイルしています。その後、1つのバイトコードと、このスタックベースインタプリタを実行します。
Pythonプログラムが実行され、バイトコードインタプリタは、コヒーレント状態のままでなければなりません。
このような調整を確実にするためにPythonのGILメカニズムを使用。
GILは、実際にはプリエンプティブマルチスレッドスイッチング動作CPythonの干渉を防止するためにミューテックスです。
いわゆるプリエンプティブマルチスレッド切替手段が別のスレッドによって割り込まれたプログラムの制御を取得するスレッドであってもよいです。
この操作の実行タイミングが不適切な干渉がある場合は、そのステータスインタプリタを弱体化させるだろう。
GILは非常に重要なマイナスの影響を与えています。
Pythonプログラムはまた、マルチスレッドをサポートしていますが、原因GILに保護されたため、同じ時間が、一つだけのスレッドが前方に実行することができます。
これは、私たちが行うにはマルチスレッド並列コンピューティングを活用するために、スピードをPYTHONしたい場合は、その後、結果は非常に残念であることを意味します。
時間インポート時間 DEF (番号)を因数分解: のために私に範囲(1、数+ 1 ): 場合は数%Iの== 0: 収率I 番号 = [2139079,1214759,151673,1852285 ] 開始 = 時間() のための数内の数字: リスト(因数分解(数)) 終了 = 時間() 印刷(' %.3f秒かかりました'%(最終スタート))
0.659秒かかりました
スレッドインポートスレッド から時間インポート時間 クラスFactorizeThread(スレッド): デフ __init__ (自己、数): スーパー()。__init__ () self.number = 数 デフ実行(自己): self.factors = リスト(因数分解(self.number)) 開始 = 時間() スレッド = [] のための番号で番号: スレッド = FactorizeThread(数) thread.start () threads.append(スレッド) のためのスレッド内のスレッド: thread.join() 終了 = 時間() 印刷(' %.3f秒かかりました'%は、(エンド開始)) 取っ 0.645秒
このような結果は、マルチスレッド標準のCPythonのインタプリタは、GILによって影響を受けることを示唆しています。
ケースということを、Pythonはなぜそれがマルチスレッドをサポートする必要がありますか?
まず、マルチスレッドは、彼らが、同時に多くのことを行うことができますようにプログラムが見えます。
第二の理由は、特定のシステムコールを実行するとき、パイソンをI / O動作の処理をブロックしているが、このような操作をトリガー。
システムコールは、プログラムがコンピュータのオペレーティングシステムは、プログラムのニーズを満たすために、外部環境と対話要求のpythonを指します。(読み取りとファイルを書き込む、通信、およびネットワーク間の表示デバイスと対話)
このブロッキング要求に応じて、オペレーティング・システムは、いくつかの時間を費やす必要があり、開発者は隔離するために、これらの時間のかかるI / O操作で、Pythonプログラムをスレッドを使用することができます。
ウィンドウと通常のコードブックを実行することはできません。select.select()実行エラー。
GILは、Pythonコードしながらは平行にすることはできませんが、そのシステムコールは、任意の負の影響を有しません。
GILは、システムコールには影響しませんので、システムコールの実装でPythonのスレッドので、GILを解放し、そして、再取得されます終了するまで待つようになっています。
スレッドに加えて、そのような組み込みasyncioモジュールなどのI / O操作を処理することができるブロッキング他の方法があります。
これらの方法は非常に大きな利点を持っているが、彼らはいくつかの努力を過ごすために持っている開発者を必要としますが、コードの別の実行モデルを再構成します。
あなたが大幅にプログラムを変更する必要はありませんが、また、並行して複数のブロックI / O操作を実行するために、達成するためにマルチスレッドを使用している場合、それはよりシンプルになります。
ハイライト:
そのため限られたグローバルインタプリタロックの1、複数のスレッドが前記複数のCPUコアで並列にバイトコードのpythonを実行できないようになっています。
2. GILを受けますが、Pythonのマルチスレッド機能は依然として有用であるが、それは簡単に複数のタスクを実行するために同じ時間の効果をシミュレートすることができます。
Pythonのスレッドによって3は、我々はいくつかの演算を実行し、システムコールの平行な複数の同時I / O操作をブロック実行することを可能にするプログラムを実行することができます。
データ競合を防ぐために、スレッドのロックで第38条
後グローバルインタプリタロック機構を理解し、多くのPythonプログラミングの初心者は考えるかもしれない:自分のPythonコードを書くときに、ミューテックスを使用する必要はありません。
真実はそうではないことに注意してください。
実際には、GILとは、開発者自身によって書かれたコードを保護しません。
同じコースの時間、実行されるのみPythonのスレッドが、このスレッドは、データ構造を操作しているとき、他のスレッドは、それを中断して、
換言すれば、二つの連続するバイトコード命令の実装にPythonインタプリタは、他のスレッドが突然中央に挿入することができます。
開発者が複数のスレッドから同時にオブジェクトにアクセスしようとした場合には、上記の状況が危険な結果につながります。
この混乱は、関連するデータ構造がその一貫性を維持することができないように、イベントでは、それは、プログラムの状態を破壊する、いつでも発生する可能性があります。
輸入スレッド、ロックをスレッドから
の時間インポート時から
:クラスカウンター
デフ__init __(自己):
self.count = 0
DEFインクリメント(自己、オフセット):
self.count + =オフセット
デフ労働者(sernsor_index、how_many、カウンタ):
_のために範囲内(how_many):
counter.increment(1)
DEF run_threads(FUNC、how_many、カウンタ):
スレッド= []
のIの範囲内(5):
引数=(I、how_many、カウンタ)
スレッド=スレッド(目標= FUNC 、引数=引数)
threads.append(スレッド)
thread.start()
スレッド内のスレッドのための:
thread.join()
how_many = 10 ** 5
カウンタ=カウンタ()
()開始=時間
run_threads(労働者、how_many、カウンタ)
終了=時間()
印刷( 'カウンターは%dである必要があり、見つかっ%D' %(5 * how_many、counter.count))
のプリントが( '取っ%.3f秒「%(最終スタート))
カウンタが500000である必要があり、449003を見つけ 0.133秒かかりました
すべてのスレッドがかなり実行することが可能であることを保証するために、Pythonインタプリタは、各スレッドにほぼ等しい処理時間を与えます。
スレッドが実行されたときに、このような割り当て戦略を達成するために、Pythonのシステムは、それを一時停止し、次にダウン継続する別のスレッドを作るかもしれません。
問題は、開発者が正確にPythonが中断してたときにこれらのスレッドされます知ることができないということです。
いくつかの操作がありますが、それはアトミック操作のように見えますが、システムは、Pythonにそれがスレッドを一時停止します半分の時間を実行することも可能です。だから、上記の状況ということが起こりました。
開発者は破壊から自分のデータ構造を保護することができるようにこのようなデータ競争的行為を防止するためには、Pythonは、内蔵のモジュールをスレッドのツールの強力なセットを提供します。
その中でも、最も簡単で便利なツールは、ミューテックスに相当する、ロッククラスです。
複数のスレッドが値の値にアクセスするように、私たちは、被害の値、カウンタオブジェクトを保護するためのミューテックスを使用することができません。
それだけで一つのスレッドがロックを取得することができます。
取得およびリリースミューテックスを、書き、人々が見やすいコードを読むことができますするステートメントを使用してサンプルコード:あなたはミューテックスを持っている糸、コード実行のその部分は何ですか。
スレッド輸入スレッド、ロック から時間インポート時間 クラス:LockingCounter デフ __init__ (自己): self.lock = ロック() self.count = 0 デフ増分(自己、オフセット): self.lock持つ: self.countは + = オフセット デフワーカー(sernsor_index、how_many、カウンタ) のための _ 中範囲(how_many): counter.increment( 1 ) DEF run_threads(FUNC、how_many、カウンタ): スレッド = [] のための I における範囲(5 :) 引数 = (I、how_many、カウンタ) スレッド =スレッド(目標= FUNC、引数= 引数) threads.append(スレッド) thread.start() のためのスレッドに:スレッド のスレッド。参加する() how_many = 10 ** 5 カウンタ = LockingCounter() 開始 = ()時間 run_threads(労働者、how_many、カウンタ) 終了 = 時間() 印刷(' カウンターは%dである必要があり、見つかっ%D '%(5 *how_many、counter.count)) のプリントが(' 秒.3f%を取った'%(最終スタート))
カウンタが500000である必要があり、500000を見つけ 2.697秒かかりました
このような動作の結果、我々は答えを望むものです。
このように、ロックデータオブジェクトは、競争の問題を解決します。
(ただし、20回の割合の違いを見つけるための追加時間統計の機能は、私は約5倍の差のみを考えました)
ハイライト:
1.pythonは、グローバルインタプリタロックを持っていますが、独自のプログラムを書くときに、まだ同じデータに対して複数のスレッドの競合を防ぐために試してみて下さい。
2.ロックは、複数のスレッドが同じオブジェクトを変更することができなければ、プログラムのデータ構造が損なわれることがあります。
3. Pythonのスレッドモジュールは、それがミューテックスを達成するための標準的なアプローチを使用して、ロックと呼ばれるクラスがあり、内蔵します。