簡體   English   中英

我的Python進程在哪些CPU內核上運行?

[英]On what CPU cores are my Python processes running?

設置

我已經用Python(在Windows PC上)編寫了一個非常復雜的軟件。 我的軟件基本上啟動了兩個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也啟動一個子Main_thread 這可以通過以下方式完成:

p = subprocess.Popen(['python', mySubprocessPath], shell=True)

從Python文檔中,我了解到該子進程在單獨的Python解釋器會話/外殼中同時運行(!) 此子Main_thread中的Main_thread完全專用於我的GUI。 GUI啟動所有通信的TCP_thread

我知道事情會變得有些復雜。 因此,我在此圖中總結了整個設置:

在此處輸入圖片說明


關於此設置,我有幾個問題。 我將在這里列出它們:

問題1 [ 解決 ]

Python解釋器一次只使用一個CPU內核來運行所有線程,這是真的嗎? 換句話說, Python interpreter session 1 (從圖中)是否將在一個CPU內核上運行所有3個線程( Main_threadTCP_threadUDP_thread )?

答:是的,這是真的。 GIL(全局解釋器鎖定)確保所有線程一次運行在一個CPU內核上。

問題2 [ 尚未解決 ]

我有辦法跟蹤它是哪個CPU內核嗎?

問題3 [ 部分解決 ]

對於這個問題,我們忘記了線程 ,但是我們專注於Python中的子進程機制。 啟動一個新的子進程意味着啟動一個新的Python解釋器實例 它是否正確?

答:是的,這是正確的。 最初,對於以下代碼是否會創建新的Python解釋器實例有些困惑:

    p = subprocess.Popen(['python', mySubprocessPath], shell = True)

該問題已得到澄清。 這段代碼確實啟動了一個新的Python解釋器實例。

Python是否足夠聰明,可以使單獨的Python解釋器實例在不同的CPU內核上運行? 有沒有一種方法可以跟蹤哪一個,也許還有一些零星的打印語句?

問題4 [ 新問題 ]

社區討論提出了一個新問題。 產生新進程時(在新的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,))

第二種方法有一個明顯的缺點,那就是它只針對一個函數-而我需要打開一個新的Python腳本。 無論如何,兩種方法在實現目標上是否相似?

問: Python解釋器一次只使用一個CPU內核來運行所有線程,這是真的嗎?

不可以。GIL和CPU關聯性是不相關的概念。 無論如何,在阻止I / O操作,C擴展內部需要大量CPU密集型計算的過程中,都可以釋放GIL。

如果線程在GIL上被阻止; 它可能不在任何CPU內核上,因此可以說,純Python多線程代碼在CPython實現中一次只能使用一個CPU內核。

問:換句話說,Python解釋器會話1(從圖中)是否將在一個CPU內核上運行所有3個線程(Main_thread,TCP_thread和UDP_thread)?

我認為CPython不會隱式管理CPU親和力。 可能依賴於OS調度程序來選擇在哪里運行線程。 Python線程是在真實OS線程之上實現的。

問:還是Python解釋器能夠將它們分布在多個內核上?

要找出可用的CPU數量:

>>> import os
>>> len(os.sched_getaffinity(0))
16

同樣,是否在不同的CPU上調度線程並不取決於Python解釋器。

問:假設問題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

問:對於這個問題,我們忘記了線程,但是我們專注於Python中的子進程機制。 啟動一個新的子進程意味着啟動一個新的Python解釋器會話/ shell。 它是否正確?

是。 subprocess進程模塊創建新的OS進程。 如果您運行python可執行文件,則它將啟動一個新的Python交互器。 如果運行bash腳本,則不會創建新的Python解釋器,即,運行bash可執行文件不會啟動新的Python解釋器/會話/等。

問:假設它是正確的,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.foomultiprocessing.Process

但是,如何將其與p = subprocess.Popen(..)進行比較? 如果我使用subprocess.Popen(..)相對於multiprocessing.Process(..)來啟動新進程(或者我應該說“ python解釋器實例”),這有關系嗎?

multiprocessing.Process()可能在subprocess.Popen()之上實現。 multiprocessing提供類似於threading API的API,並且抽象化了python進程之間的通信細節(如何序列化Python對象以在進程之間發送)。

如果沒有CPU密集型任務,則可以在單個進程中運行GUI和I / O線程。 如果您有一系列占用大量CPU的任務, 則要一次使用多個CPU,請使用具有C擴展名的多個線程,例如lxmlregexnumpy (或您自己的使用Cython創建的),這些線程可以在長時間計算時釋放GIL或將其卸載分為單獨的進程(一種簡單的方法是使用進程池,例如由concurrent.futures提供)。

問:社區討論提出了一個新問題。 產生新進程時(在新的Python解釋器實例中)顯然有兩種方法:

 # Approach 1(a) p = subprocess.Popen(['python', mySubprocessPath], shell = True) # Approach 1(b) (JF Sebastian) p = subprocess.Popen([sys.executable, mySubprocessPath]) # Approach 2 p = multiprocessing.Process(target=foo, args=(q,)) 

在POSIX上, “方法1(a)”是錯誤的(盡管在Windows上可以使用)。 為了實現可移植性,除非您知道需要cmd.exe (在這種情況下傳遞一個字符串,以確保使用正確的命令行轉義),否則請使用“方法1(b)”

第二種方法有一個明顯的缺點,那就是它只針對一個函數-而我需要打開一個新的Python腳本。 無論如何,兩種方法在實現目標上是否相似?

subprocess進程創建新進程,例如, 任何進程都可以運行bash腳本。 multprocessing用於在另一個進程中運行Python代碼。 導入 Python模塊並運行其功能比將其作為腳本運行更靈活。 請參閱使用子進程在python腳本中使用輸入調用python腳本

由於您正在使用在thread上構建的threading模塊。 如文檔所示,它使用操作系統的“ POSIX線程實現” pthread

  1. 線程由操作系統而不是Python解釋器管理。 因此,答案將取決於系統中的pthread庫。 但是,CPython使用GIL來防止多個線程同時執行Python字節碼。 因此它們將被順序化。 但是仍然可以將它們分為不同的內核,具體取決於您的pthread庫。
  2. 簡單地使用調試器並將其附加到您的python.exe。 例如GDB thread命令
  3. 與問題1相似,新進程由操作系統管理,並且可能在其他內核上運行。 使用調試器或任何進程監視器查看它。 有關更多詳細信息,請轉到CreatProcess()文檔頁面

1:2,您有3個實際線程,但是在CPython中,它們受GIL的限制,因此,假設它們運行的​​是純python,則您將看到CPU使用率,就像僅使用一個內核一樣。

3:正如gdlmx所說,要由操作系統選擇運行線程的核心,但是如果您確實需要控制,則可以通過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 ,我在這里使用私有Popen._handle。 干凈的方法是OpenProcess(p.tid)等。

是的, subprocess進程在另一個新進程中像其他所有進程一樣運行python。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM