セットアップ
Python(Windows PC))でかなり複雑なソフトウェアを作成しました。私のソフトウェアは基本的に2つのPythonインタープリターシェルを起動します。最初のシェルが起動します(おそらく)main.py
ファイルをダブルクリックすると、そのシェル内で他のスレッドが次のように開始されます。
# Start TCP_thread
TCP_thread = threading.Thread(name = 'TCP_loop', target = TCP_loop, args = (TCPsock,))
TCP_thread.start()
# Start UDP_thread
UDP_thread = threading.Thread(name = 'UDP_loop', target = UDP_loop, args = (UDPsock,))
TCP_thread.start()
Main_thread
は、TCP_thread
とUDP_thread
を開始します。これらは個別のスレッドですが、すべて単一のPython Shell。
Main_thread
もサブプロセスを開始します。これは次の方法で行われます。
p = subprocess.Popen(['python', mySubprocessPath], Shell=True)
Pythonドキュメントから、このサブプロセスは同時に(!)別のPythonインタプリタセッション/シェル:このサブプロセスのMain_thread
は完全に私のGUI専用であり、GUIはすべての通信に対してTCP_thread
を開始します。
私は物事が少し複雑になることを知っています。したがって、セットアップ全体をこの図にまとめました。
このセットアップに関していくつか質問があります。ここにリストします。
質問1[Solved]
Pythonインタープリターは一度に1つのCPUコアのみを使用してすべてのスレッドを実行しますか?つまり、Python interpreter session 1
(図から)は3つのスレッドすべてを実行します(Main_thread
、TCP_thread
、およびUDP_thread
)1つのCPUコアで?
回答:はい、これは本当です。 GIL(Global Interpreter Lock)は、すべてのスレッドが一度に1つのCPUコアで実行されるようにします。
質問2[未解決]
どのCPUコアであるかを追跡する方法はありますか?
質問3[部分的に解決した]
この質問では、threadsを忘れていますが、Pythonのsubprocessメカニズムに焦点を当てています。新しいサブプロセスの開始は、新しいPythonインタープリターinstance)の開始を意味します。これは正しいですか?
回答:はい、これは正しいです。最初は、次のコードが新しいPythonインタープリターインスタンスを作成するかどうかについて混乱がありました:
p = subprocess.Popen(['python', mySubprocessPath], Shell = True)
問題は明確になりました。このコードは、実際に新しいPythonインタープリターインスタンスを開始します。
Python別個のPythonインタープリターインスタンスを別のCPUコアで実行するのに十分なほど賢いでしょうか?文も印刷しますか?
質問4[新しい質問]
コミュニティの議論は新しい質問を提起しました。新しいプロセスを生成するとき、明らかに2つのアプローチがあります(新しいPythonインタープリターインスタンス内):
# Approach 1(a)
p = subprocess.Popen(['python', mySubprocessPath], Shell = True)
# Approach 1(b) (J.F. Sebastian)
p = subprocess.Popen([sys.executable, mySubprocessPath])
# Approach 2
p = multiprocessing.Process(target=foo, args=(q,))
2番目のアプローチには、関数のみを対象とするという明らかな欠点がありますが、新しいPythonスクリプトを開く必要があります。とにかく、どちらのアプローチも達成方法が似ていますか?
Q:Pythonインタープリターは、すべてのスレッドを実行するために一度に1つのCPUコアのみを使用するのは本当ですか?
いいえ。GILとCPUアフィニティは無関係な概念です。とにかく、GILはI/O操作のブロック中、C拡張内での長いCPU集中型計算中に解放できます。
GILでスレッドがブロックされている場合;おそらくどのCPUコア上にもないので、純粋なPythonマルチスレッドコードはCPython実装で一度に1つのCPUコアしか使用できないと言っても過言ではありません。
Q:つまり、Pythonインタープリターセッション1(図から)は3つのスレッド(Main_thread、TCP_thread、 UDP_thread)1つのCPUコアで?
CPythonが暗黙的にCPUアフィニティを管理するとは思わない。スレッドの実行場所の選択は、OSスケジューラに依存している可能性があります。 Pythonスレッドは、実際のOSスレッドの上に実装されます。
Q:またはPythonインタープリターはそれらを複数のコアに分散できますか?
使用可能なCPUの数を調べるには:
_>>> import os
>>> len(os.sched_getaffinity(0))
16
_
繰り返しますが、スレッドが異なるCPUでスケジュールされているかどうかは、Pythonインタープリターに依存しません。
Q:質問1の答えが「複数のコア」であると仮定すると、各スレッドが実行されているコアを追跡する方法はありますか?散発的な印刷文質問1の答えが「1つのコアのみ」である場合、どのコアかを追跡する方法はありますか?
あるタイムスロットから別のタイムスロットに特定のCPUが変わる可能性があります。 古いLinuxカーネルの_/proc/<pid>/task/<tid>/status
_のようなものを見ることができます 。私のマシンでは、 _task_cpu
_は_/proc/<pid>/stat
_または_/proc/<pid>/task/<tid>/stat
_ から読み取ることができます。
_>>> open("/proc/{pid}/stat".format(pid=os.getpid()), 'rb').read().split()[-14]
'4'
_
現在のポータブルソリューションについては、 psutil
がそのような情報を公開しているかどうかを確認してください。
現在のプロセスを一連のCPUに制限できます。
_os.sched_setaffinity(0, {0}) # current process on 0-th core
_
Q:この質問では、スレッドについては忘れていますが、Pythonのサブプロセスメカニズムに焦点を当てています。新しいサブプロセスを開始すると、新しいPythonインタープリターセッション/シェルが開始されます。これは正しいです?
はい。 subprocess
モジュールは、新しいOSプロセスを作成します。 python
実行可能ファイルを実行すると、新しいPython interpeterが起動します。 bashスクリプトを実行すると、新しいPythonインタープリターは作成されません。つまり、bash
実行可能ファイルを実行しても、新しいPythonインタープリター/セッション/などは開始されません。
Q:正しいと仮定すると、Pythonは、その別個のインタープリターセッションを別のCPUコアで実行できるようになりますか?おそらく散発的な印刷文を使用して、これを追跡する方法はありますか?
上記を参照してください(つまり、OSがスレッドの実行場所を決定し、スレッドの実行場所を公開するOS APIが存在する可能性があります)。
multiprocessing.Process(target=foo, args=(q,)).start()
_multiprocessing.Process
_は、新しいOSプロセス(新しいPythonインタープリターを実行する)も作成します。
実際には、私のサブプロセスは別のファイルです。したがって、この例は私には機能しません。
Pythonはモジュールを使用してコードを整理します。コードが_another_file.py
_にある場合は、メインモジュールで_import another_file
_を使用し、_another_file.foo
_を_multiprocessing.Process
_に渡します。
それでも、p = subprocess.Popen(..)とどう比較しますか? subprocess.Popen(..)vs multiprocessing.Process(..)で新しいプロセスを開始するか(または 'pythonインタープリターインスタンス'と言う必要がありますか?)
multiprocessing.Process()
はおそらくsubprocess.Popen()
の上に実装されます。 multiprocessing
はthreading
APIに似たAPIを提供し、pythonプロセス間の通信の詳細を抽象化します(送信するためにPythonオブジェクトをシリアル化する方法プロセス間)。
CPUを集中的に使用するタスクがない場合は、単一のプロセスでGUIスレッドとI/Oスレッドを実行できます。 CPUを集中的に使用する一連のタスクがある場合、複数のCPUを一度に使用するには、lxml
、regex
、numpy
(または独自の Cython )を使用して作成されたもので、長い計算中にGILを解放したり、個別のプロセスにオフロードしたりできます(簡単な方法は、 _concurrent.futures
_ )。
Q:コミュニティの議論で新しい質問が提起されました。新しいプロセスを(新しいPythonインタープリターインスタンス内で)生成するとき、明らかに2つのアプローチがあります。
_# Approach 1(a) p = subprocess.Popen(['python', mySubprocessPath], Shell = True) # Approach 1(b) (J.F. Sebastian) p = subprocess.Popen([sys.executable, mySubprocessPath]) # Approach 2 p = multiprocessing.Process(target=foo, args=(q,))
_
"Approach 1(a)"はPOSIXでは間違っています(Windowsでは動作する可能性があります)。移植性のために、_cmd.exe
_が必要なことがわかっている場合を除き、"Approach 1(b)"を使用します(この場合、正しいコマンドラインエスケープが使用されるように文字列を渡します)。
2番目のアプローチには、関数のみを対象とするという明らかな欠点がありますが、新しいPythonスクリプトを開く必要があります。とにかく、両方のアプローチは達成するものが似ていますか?
subprocess
は新しいプロセスを作成します。anyプロセス。たとえば、bashスクリプトを実行できます。 multprocessing
は、別のプロセスでPythonコードを実行するために使用されます。 import a Pythonモジュールの方が柔軟であり、スクリプトとして実行するよりも機能を実行します。 subprocess を使用してpythonスクリプトで入力を使用してpythonスクリプトを呼び出すを参照してください。
threading
上に構築されるthread
モジュールを使用しているため。ドキュメントが示唆するように、OSの '' POSIXスレッド実装 ''pthreadを使用します。
CreatProcess()
documentation page にアクセスしてください。1、2:実際のスレッドは3つありますが、CPythonではGILによって制限されているため、純粋なpythonを実行していると仮定すると、コードはCPU使用率を1つのコアのみが使用されているかのように表示します。
3:gdlmxで述べたように、スレッドを実行するコアを選択するのはOS次第ですが、本当に制御が必要な場合は、ctypes
経由でネイティブAPIを使用してプロセスまたはスレッドアフィニティを設定できます。 Windowsを使用しているため、次のようになります。
_# This will run your subprocess on core#0 only
p = subprocess.Popen(['python', mySubprocessPath], Shell = True)
cpu_mask = 1
ctypes.windll.kernel32.SetProcessAffinityMask(p._handle, cpu_mask)
_
ここでは、単純にするためにプライベート_Popen._handle
_を使用します。きれいな方法はbeOpenProcess(p.tid)
などです。
はい、subprocess
はpython別の新しいプロセスの他のすべてと同じように実行されます。