Python Language
Pythonの同時実行性
サーチ…
備考
Python開発者は、 threading
とmultiprocessing
間のAPIが類似しているため、プログラマにとって2つの変種の切り替えが簡単になりました。
スレッドモジュール
from __future__ import print_function
import threading
def counter(count):
while count > 0:
print("Count value", count)
count -= 1
return
t1 = threading.Thread(target=countdown,args=(10,))
t1.start()
t2 = threading.Thread(target=countdown,args=(20,))
t2.start()
I は L OCKをnterpreter GIL、またはGのローブとして知られるものを使用するので、このようなCPythonのようなパイソンの特定の実装では、真の並列処理は、スレッドを使用して達成されません。
Pythonの並行処理の優れた概要を以下に示します。
David BeazleyによるPythonの同時実行性(YouTube)
マルチプロセッシングモジュール
from __future__ import print_function
import multiprocessing
def countdown(count):
while count > 0:
print("Count value", count)
count -= 1
return
if __name__ == "__main__":
p1 = multiprocessing.Process(target=countdown, args=(10,))
p1.start()
p2 = multiprocessing.Process(target=countdown, args=(20,))
p2.start()
p1.join()
p2.join()
ここでは、各機能は新しいプロセスで実行されます。 Python VMの新しいインスタンスがコードを実行しているので、 GIL
は存在せず、複数のコアで並列処理が実行されます。
Process.start
メソッドは、この新しいプロセスを起動し、引数args
を使用してtarget
引数で渡された関数を実行します。 Process.join
メソッドは、プロセスp1
とp2
実行の終了を待ちます。
新しいプロセスは、パイソンのバージョンとコードが例えば実行されているplateformに応じて異なる起動されます:
- Windowsは新しいプロセスを作成するために
spawn
を使用します。 - UNIXシステムと3.3より前のバージョンでは、
fork
を使用してプロセスが作成されます。
このメソッドはforkのPOSIX使用法を尊重しないため、特に他のマルチプロセッシングライブラリと相互作用するときに予期しない動作につながることに注意してください。 - UNIXシステムとバージョン3.4以降では、プログラムの最初に
multiprocessing.set_start_method
を使用してfork
、forkserver
またはspawn
いずれかを使って新しいプロセスを開始することができます。forkserver
とspawn
メソッドはforkよりも遅くなりますが、予期しない動作を避けます。
POSIX forkの使用法 :
マルチスレッドプログラムのfork後、execveを呼び出すまで安全にasync-signal-safe関数だけを安全に呼び出すことができます。
( 参照 )
forkを使用すると、現在のすべてのmutexに対して全く同じ状態で新しいプロセスが起動されますが、 MainThread
だけがMainThread
されます。それがレースの条件などにつながる可能性があるので、これは安全ではありません。
-
MainThread
でLock
を使用し、それをある時点でロックしようとしている他のスレッドに渡すとします。fork
同時に発生すると、新しいプロセスはロックされたロックから開始します。ロックされたロックは、この新しいプロセスに2番目のスレッドが存在しないため解放されません。
実のところ、この種の動作は純粋なPythonではmultiprocessing
が適切に処理するためには起こらないはずですが、他のライブラリとやりとりしている場合、この種の動作が起こり、システムがクラッシュすることがあります。
マルチプロセッシングプロセス間でデータを渡す
データは2つのスレッド間で処理されるときに敏感なので(競合状態を引き起こして同時読み取りと同時書き込みが競合する可能性があると考えます)、スレッド間でデータのやりとりを容易にするために一意のオブジェクトが作成されました。本当のアトミック操作はすべてスレッド間で使用できますが、常にQueueに固執することは常に安全です。
import multiprocessing
import queue
my_Queue=multiprocessing.Queue()
#Creates a queue with an undefined maximum size
#this can be dangerous as the queue becomes increasingly large
#it will take a long time to copy data to/from each read/write thread
大部分の人は、キューを使用するときは、空のデータを使用するのではなく、try:except:ブロックにキューのデータを置くことを推奨します。ただし、スキャンサイクルをスキップするかどうかは関係ありません(データがqueue.Empty==True
、 queue.Empty==False
状態を反転している間にキューに置くことができます) Iftryブロックと呼ばれるもので書き込みアクセスを行うことができます。なぜなら、 'if'ステートメントは例外をキャッチするよりも技術的に優れているからです。
import multiprocessing
import queue
'''Import necessary Python standard libraries, multiprocessing for classes and queue for the queue exceptions it provides'''
def Queue_Iftry_Get(get_queue, default=None, use_default=False, func=None, use_func=False):
'''This global method for the Iftry block is provided for it's reuse and
standard functionality, the if also saves on performance as opposed to catching
the exception, which is expencive.
It also allows the user to specify a function for the outgoing data to use,
and a default value to return if the function cannot return the value from the queue'''
if get_queue.empty():
if use_default:
return default
else:
try:
value = get_queue.get_nowait()
except queue.Empty:
if use_default:
return default
else:
if use_func:
return func(value)
else:
return value
def Queue_Iftry_Put(put_queue, value):
'''This global method for the Iftry block is provided because of its reuse
and
standard functionality, the If also saves on performance as opposed to catching
the exception, which is expensive.
Return True if placing value in the queue was successful. Otherwise, false'''
if put_queue.full():
return False
else:
try:
put_queue.put_nowait(value)
except queue.Full:
return False
else:
return True