我的 Python 进程在哪些 CPU 内核上运行?

我用 Python(在 Windows PC 上)编写了一个相当复杂的软件。我的软件基本上启动了两个 Python 解释器 shell。当您双击main.py文件时,第一个 shell 会启动(我想)。在该外壳中,其他线程以下列方式启动:

# 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 解释器会话/shell 中同时运行(!)。在Main_thread这个子完全是献给我的GUI。GUITCP_thread为其所有通信启动一个。

我知道事情变得有点复杂。因此,我在此图中总结了整个设置:

在此处输入图像描述

我有几个关于这个设置的问题。我将在这里列出它们:

问题1 [已解决]

Python 解释器一次只使用一个 CPU 内核来运行所有线程是真的吗?换句话说,将Python interpreter session 1(从图)运行所有3个线程(Main_thread,TCP_thread并UDP_thread在一个CPU核心)?

回答:是的,这是真的。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 亲和性是不相关的概念。GIL 可以在阻塞 I/O 操作期间释放,无论如何在 C 扩展中进行长时间的 CPU 密集型计算。

如果一个线程在 GIL 上被阻塞;它可能不在任何 CPU 内核上,因此可以公平地说纯 Python 多线程代码在 CPython 实现中一次只能使用一个 CPU 内核。

问:换句话说,Python 解释器会话 1(从图中)是否会在一个 CPU 内核上运行所有 3 个线程(Main_thread、TCP_thread 和 UDP_thread)?

我不认为 CPython 隐式管理 CPU 亲和力。它可能依赖于操作系统调度程序来选择在哪里运行线程。Python 线程是在真正的 OS 线程之上实现的。

问:或者 Python 解释器是否能够将它们分布在多个内核上?

要找出可用 CPU 的数量:

>>> import os
>>> len(os.sched_getaffinity(0))
16
同样,线程是否调度在不同的 CPU 上并不取决于 Python 解释器。

问:假设问题 1 的答案是“多核”,我是否可以通过一些零星的打印语句来跟踪每个线程在哪个核上运行?如果问题 1 的答案是“只有一个核心”,我是否有办法跟踪它是哪一个?

我想,一个特定的 CPU 可能会从一个时隙变为另一个时隙。您可以查看类似/proc//task//status旧 Linux 内核的内容。在我的机器上,task_cpu可以从/proc//stator读取/proc//task//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模块创建新的操作系统进程。如果您运行python可执行文件,那么它会启动一个新的 Python 解释器。如果您运行 bash 脚本,则不会创建新的 Python 解释器,即,运行bash可执行文件不会启动新的 Python 解释器/会话/等。

问:假设它是正确的,Python 是否足够聪明,可以让单独的解释器会话在不同的 CPU 内核上运行?有没有办法跟踪这个,也许还有一些零星的打印声明?

见上文(即,操作系统决定在哪里运行您的线程,并且可能有操作系统 API 公开线程运行的位置)。

multiprocessing.Process(target=foo, args=(q,)).start()

multiprocessing.Process 还会创建一个新的操作系统进程(运行一个新的 Python 解释器)。

实际上,我的子进程是另一个文件。所以这个例子对我不起作用。

Python 使用模块来组织代码。如果您的代码在another_file.py然后import another_file在您的主模块中并传递another_file.foo给multiprocessing.Process.

不过,您如何将其与 p = subprocess.Popen(..) 进行比较?如果我使用 subprocess.Popen(..) 与 multiprocessing.Process(..) 启动新进程(或者我应该说“python 解释器实例”),这有关系吗?

multiprocessing.Process()很可能在subprocess.Popen(). multiprocessing提供类似于threadingAPI 的 API,它抽象出 Python 进程之间的通信细节(Python 对象如何序列化以在进程之间发送)。

如果没有 CPU 密集型任务,那么您可以在单个进程中运行您的 GUI 和 I/O 线程。如果您有一系列 CPU 密集型任务,那么要一次使用多个 CPU,请使用具有 C 扩展名的多个线程,例如lxml, regex,numpy(或您自己使用Cython创建的线程)可以在长时间计算期间释放 GIL 或将它们卸载到单独的进程中(一种简单的方法是使用由 提供的进程池concurrent.futures)。

问:社区讨论提出了一个新问题。生成新进程时(在新的 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,))
“方法 1(a)”在 POSIX 上是错误的(尽管它可能在 Windows 上工作)。为了可移植性,请使用“方法 1(b)”,除非您知道需要cmd.exe(在这种情况下传递一个字符串,以确保使用正确的命令行转义)。

第二种方法有一个明显的缺点,它只针对一个函数——而我需要打开一个新的 Python 脚本。无论如何,这两种方法在实现的目标上是否相似?

subprocess创建新进程,任何进程,例如,您可以运行 bash 脚本。multprocessing用于在另一个进程中运行 Python 代码。导入Python 模块并运行其函数比将其作为脚本运行更灵活。请参阅使用 subprocess 在 python 脚本中调用带有输入的 python 脚本。

要回复问题请先登录注册