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

时间:2016-04-22 13:23:33

标签: python multithreading python-3.x multiprocessing

设置

我用Python编写了一个相当复杂的软件(在Windows PC上)。我的软件基本上启动了两个Python解释器shell。双击main.py文件时,第一个shell启动(我猜)。在该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_threadUDP_thread。虽然这些是单独的线程,但它们都在一个Python shell中运行。

Main_thread也启动子进程。这是通过以下方式完成的:

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

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

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

enter image description here

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

问题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是否足够聪明,可以在不同的CPU核心上运行单独的Python解释器实例?有没有办法跟踪哪一个,也许有一些零星的印刷陈述?

问题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脚本。无论如何,这两种方法都取得了类似的效果吗?

3 个答案:

答案 0 :(得分:25)

  

问: 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亲和力。它可能依赖于OS调度程序来选择运行线程的位置。 Python线程是在真实操作系统线程之上实现的。

  

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

找出可用CPU的数量:

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

同样,线程是否在不同的CPU上进行调度并不依赖于Python解释器。

  

问:假设问题1的答案是“多核心”,我是否有办法跟踪每个线程正在运行的核心,可能是偶尔会有一些打印声明?如果对问题1的回答是“只有一个核心”,我是否有办法追踪它是哪一个?

我想,一个特定的CPU可能会从一个时隙变为另一个时隙。你可以look at something like /proc/<pid>/task/<tid>/status on old Linux kernels。在我的机器上,task_cpu can be read from /proc/<pid>/stat or /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 interpeter。如果运行bash脚本,则不会创建新的Python解释器,即运行bash可执行文件不会启动新的Python解释器/会话/等。

  

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

见上文(即操作系统决定在哪里运行你的线程,并且可能有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(..)和multiprocessing.Process(..)启动新进程(或者我应该说&#39; python解释器实例&#39;),这是否重要?

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) (J.F. Sebastian)
p = subprocess.Popen([sys.executable, mySubprocessPath])

# Approach 2
p = multiprocessing.Process(target=foo, args=(q,))

&#34;方法1(a)&#34; 在POSIX上出错(尽管它可能适用于Windows)。为了便于携带,请使用&#34;方法1(b)&#34; 除非您知道需要cmd.exe(在这种情况下传递一个字符串,以确保正确的命令 - 使用了线逃逸。)

  

第二种方法有一个明显的缺点,它只针对一个函数 - 而我需要打开一个新的Python脚本。无论如何,这两种方法都取得了类似的效果吗?

subprocess创建新进程,任何进程,例如,您可以运行bash脚本。 multprocessing用于在另一个进程中运行Python代码。 导入 Python模块并运行其功能比将其作为脚本运行更灵活。请参阅Call python script with input with in a python script using subprocess

答案 1 :(得分:3)

由于您使用的是在threading上构建的thread模块。如文档所示,它使用操作系统的“POSIX线程实现” pthread

  1. 线程由OS而不是Python解释器管理。所以答案取决于系统中的pthread库。但是,CPython使用GIL来防止多个线程同时执行Python字节码。所以它们将被顺序化。但是它们仍然可以分成不同的核心,这取决于你的pthread库。
  2. 简单地使用调试器并将其附加到python.exe。例如GDB thread command
  3. 与问题1类似,新流程由您的操作系统管理,可能在不同的核心上运行。使用调试器或任何进程监视器来查看它。有关详细信息,请转到CreatProcess()文档page

答案 2 :(得分:1)

1,2:你有三个真正的线程,但是在CPython中它们受GIL限制,因此,假设它们正在运行纯python代码,你会看到CPU的使用情况,好像只有一个使用的核心。

3:正如所说的gdlmx,操作系统选择核心来运行线程, 但如果您真的需要控制,可以使用设置进程或线程关联 原生API通过ctypes。因为你在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来表示简单。干净的方式是OpenProcess(p.tid)等。

是的,subprocess像其他新进程中的其他所有内容一样运行python。