设置
我用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的数量,请执行以下操作:
>&燃气轮机&燃气轮机;导入操作系统
&燃气轮机&燃气轮机&燃气轮机;len(os.sched_getaffinity(0))
16
同样,线程是否调度在不同的CPU上并不取决于Python解释器
Q:假设问题1的答案是“多核”,我是否有办法跟踪每个线程在哪个核上运行,也许是使用一些零星的打印语句?如果问题1的答案是“只有一个核心”,我是否有办法追踪它是哪一个
我想,一个特定的CPU可能会从一个时隙切换到另一个时隙。您可以查看类似于/proc/<;pid>/任务/<;工业贸易署/旧Linux内核上的状态。在我的机器上,可以从/proc/<;pid>/stat或/proc/<;pid>/任务/<;工业贸易署/统计数据:
>&燃气轮机&燃气轮机;打开(“/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扩展的线程,例如lxml、regex、numpy(或者您自己使用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子流程