K.M*_*ier 37 python multithreading multiprocessing python-3.x
设置
我用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()
Run Code Online (Sandbox Code Playgroud)
在Main_thread启动TCP_thread和UDP_thread.虽然这些是单独的线程,但它们都在一个单独的Python shell中运行.
这Main_thread也启动了一个子进程.这是通过以下方式完成的:
p = subprocess.Popen(['python', mySubprocessPath], shell=True)
Run Code Online (Sandbox Code Playgroud)
从Python文档中,我了解到这个子进程在一个单独的Python解释器会话/ shell中同时运行(!).在Main_thread这个子完全是献给我的GUI.GUI TCP_thread为其所有通信启动a .
我知道事情变得有点复杂.因此,我总结了这个图中的整个设置:
关于这个设置,我有几个问题.我会在这里列出它们:
问题1 [已解决 ]
Python解释器一次只使用一个CPU核心来运行所有线程,这是真的吗?换句话说,Python interpreter session 1(从图中)是否会在一个CPU核心上运行所有3个线程(Main_thread,TCP_thread和UDP_thread)?
答:是的,这是真的.GIL(全局解释器锁定)确保所有线程一次在一个CPU核心上运行.
问题2 [ 尚未解决 ]
我有办法跟踪它是哪个CPU核心吗?
问题3 [ 部分解决 ]
对于这个问题,我们忘记了线程,但我们专注于Python中的子进程机制.启动一个新的子进程意味着启动一个新的Python解释器实例.它是否正确?
答:是的,这是正确的.首先,关于以下代码是否会创建新的Python解释器实例存在一些混淆:
p = subprocess.Popen(['python', mySubprocessPath], shell = True)
Run Code Online (Sandbox Code Playgroud)
这个问题已得到澄清.这段代码确实启动了一个新的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,))
Run Code Online (Sandbox Code Playgroud)
第二种方法有一个明显的缺点,它只针对一个函数 - 而我需要打开一个新的Python脚本.无论如何,他们实现的方法是否相似?
jfs*_*jfs 25
问: Python解释器一次只使用一个CPU核心来运行所有线程,这是真的吗?
不.GIL和CPU亲和力是不相关的概念.在阻塞I/O操作期间可以释放GIL,无论如何都要在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
Run Code Online (Sandbox Code Playgroud)
同样,线程是否在不同的CPU上进行调度并不依赖于Python解释器.
问:假设问题1的答案是"多核",我是否有办法跟踪每个线程正在运行的核心,可能还有一些零星的打印语句?如果问题1的答案是"只有一个核心",我是否有办法追踪它是哪一个?
我想,一个特定的CPU可能会从一个时隙变为另一个时隙.您可以查看/proc/<pid>/task/<tid>/status旧Linux内核上的内容.在我的机器上,task_cpu可以读取/proc/<pid>/stat或/proc/<pid>/task/<tid>/stat:
>>> open("/proc/{pid}/stat".format(pid=os.getpid()), 'rb').read().split()[-14]
'4'
Run Code Online (Sandbox Code Playgroud)
对于当前的便携式解决方案,请查看是否psutil公开此类信息.
您可以将当前进程限制为一组CPU:
os.sched_setaffinity(0, {0}) # current process on 0-th core
Run Code Online (Sandbox Code Playgroud)
问:对于这个问题,我们忘记了线程,但我们专注于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(..)启动新进程(或者我应该说'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解释器实例中)显然有两种方法:
Run Code Online (Sandbox Code Playgroud)# 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模块并运行其功能比将其作为脚本运行更灵活.请参阅使用子进程在python脚本中使用输入调用python脚本.