我的Python进程在什么CPU核上运行?

设置

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

#启动TCP#U线程
线程(名称='TCP\U循环',目标=TCP\U循环,参数=(TCPsock,))
TCP_thread.start()
#启动UDP_线程
UDP\u thread=threading.thread(name='UDP\u loop',target=UDP\u loop,args=(UDPsock,))
TCP_thread.start()

Main_线程启动一个TCP_线程和一个UDP_线程。尽管这些线程是独立的,但它们都在一个Python shell中运行

主线程也启动一个子进程。这是通过以下方式完成的:

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

从Python文档中,我了解到这个子流程在一个单独的Python解释器会话/shell中同时运行(!)。此子流程中的主线程完全专用于我的GUI。GUI为其所有通信启动一个TCP\u线程

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


关于这个设置,我有几个问题。我将在下面列出:

问题1[已解决]

Python解释器一次只使用一个CPU核来运行所有线程,这是真的吗?换句话说,Python解释器会话1(图中)是否会在一个CPU内核上运行所有3个线程(主线程TCP线程UDP线程

回答:是的,这是真的。GIL(全局解释器锁)确保所有线程一次在一个CPU内核上运行。

问题2[尚未解决]

我有办法跟踪它是哪个CPU核心吗

问题3[部分解决]

对于这个问题,我们忘记了线程,但我们主要关注Python中的子进程机制。启动一个新的子流程意味着启动一个新的Python解释器实例。这是正确的吗

回答:是的,这是正确的。起初,对于以下代码是否会创建一个新的Python解释器实例存在一些困惑:

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

问题已经澄清。这段代码确实启动了一个新的Python解释器实例。

Python是否足够聪明,可以让单独的Python解释器实例在不同的CPU核心上运行?有没有一种方法可以追踪哪一种,也许还有一些零星的打印语句

问题4[新问题]

社区讨论提出了一个新问题。在生成新进程(在新的Python解释器实例中)时,显然有两种方法:

方法1(a)
p=subprocess.Popen([‘python’,mySubprocessPath],shell=True)
#方法1(b)(J.F.塞巴斯蒂安)
p=subprocess.Popen([sys.executable,mySubprocessPath])
#方法2
p=multiprocessing.Process(target=foo,args=(q,))

第二种方法有一个明显的缺点,就是它只针对一个函数,而我需要打开一个新的Python脚本。不管怎样,这两种方法在实现目标方面是否相似

Q:Python解释器一次只使用一个CPU核来运行所有线程,这是真的吗

不。GIL和CPU关联性是不相关的概念。GIL可以在阻塞I/O操作期间释放,而阻塞I/O操作是C扩展中CPU密集型的长时间计算

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

Q:换句话说,Python解释器会话1(图中)是否会在一个CPU内核上运行所有3个线程(主线程、TCP线程和UDP线程)

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

Q:或者Python解释器能够将它们分布到多个核心上吗

要了解可用CPU的数量,请执行以下操作:

&gt&燃气轮机&燃气轮机;导入操作系统
&燃气轮机&燃气轮机&燃气轮机;len(os.sched_getaffinity(0))
16

同样,线程是否调度在不同的CPU上并不取决于Python解释器

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

我想,一个特定的CPU可能会从一个时隙切换到另一个时隙。您可以查看类似于/proc/<pid&gt/任务/<工业贸易署/旧Linux内核上的状态。在我的机器上,可以从/proc/<pid&gt/stat/proc/<pid&gt/任务/<工业贸易署/统计数据

&gt&燃气轮机&燃气轮机;打开(“/proc/{pid}/stat”.format(pid=os.getpid()),'rb').read().split()[-14]
'4'

对于当前的便携式解决方案,请参见psutil是否公开此类信息

您可以将当前进程限制为一组CPU:

os.sched_setaffinity(0,{0})#第0核上的当前进程

Q:对于这个问题,我们忘记了线程,但我们主要关注Python中的子进程机制。启动一个新的子进程意味着启动一个新的Python解释器会话/shell。这是正确的吗

对子进程模块创建新的操作系统进程。如果运行pythonexecutable,那么它将启动一个新的python interpeter。如果运行bash脚本,则不会创建新的Python解释器,即运行bashexecutable不会启动新的Python解释器/会话等

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

请参见上面的内容(即,操作系统决定线程的运行位置,并且可能存在公开线程运行位置的操作系统API)

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

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

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

Python使用模块来组织代码。如果您的代码位于另一个\u文件.py中,则在主模块中导入另一个\u文件,并将另一个\u文件.foo传递到多处理.Process

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

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

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

Q:社区讨论提出了一个新问题。显然,在生成新进程(在新的Python解释器实例中)时有两种方法:

方法1(a)
p=subprocess.Popen([‘python’,mySubprocessPath],shell=True)
#方法1(b)(J.F.塞巴斯蒂安)
p=subprocess.Popen([sys.executable,mySubprocessPath])
#方法2
p=multiprocessing.Process(target=foo,args=(q,))

“方法1(a)”在POSIX上是错误的(尽管它可以在Windows上工作)。为了便于移植,请使用“方法1(b)”,除非您知道需要cmd.exe(在这种情况下,请传递一个字符串,以确保使用了正确的命令行转义)

第二种方法有一个明显的缺点,就是它只针对一个函数,而我需要打开一个新的Python脚本

subprocess创建新进程,任何进程(例如,您可以运行bash脚本)。multprocessing用于在另一个进程中运行Python代码。导入Python模块并运行其功能比将其作为脚本运行更为灵活。请参阅使用g子流程

发表评论