本文主要介绍Python的线程模块创建多个并发线程,并研究其对此计算机CPU使用率的影响。在撰写代码之前,先来看看这台计算机上可用的处理器数量,图中的处理器核数是1个处理器,2个核心以及4个逻辑处理器。这说明这台MacBook Pro笔记本有2个独立的完整的处理器核心,每个核心支持超线程,可以独立运行两个独立的应用程序。
这2个内核中的超线程并不意味着将获得双倍的性能。超线程利用处理器的未使用部分,如果一个线程暂停或不使用特定资源,则另一个线程可能能够使用它。在某些工作负载下,这可以提升整体性能改,但也依赖于具体应用程序。蓝色和红色图表显示了这些处理器的CPU利用率总百分比。在没有太多程序的时候,使用率保持在低位,图中显示闲置接近70%左右。
如果想单独查看每个处理器的CPU使用情况,可以通过单击任务管理器底部的打开资源监视器获取更多CPU信息。下面创建一个简短的示例程序,代码如下:
#!/usr/bin/env python3
""" Threads that waste CPU cycles """
import os
import threading
# a simple function that wastes CPU cycles forever
def cpu_waster():
while True:
pass
# display information about this process
print('n Process ID: ', os.getpid())
print('Thread Count: ', threading.active_count())
for thread in threading.enumerate():
print(thread)
for i in range(4):
threading.Thread(target=cpu_waster).start()
# display information about this process
print('n Process ID: ', os.getpid())
print('Thread Count: ', threading.active_count())
for thread in threading.enumerate():
print(thread)
这里定义了一个简单的函数,叫做CPU waster,它其实一个while循环,没有做任何有用的工作,但运行该功能的线程将永远保持活跃并持续使用CPU资源。
运行程序,打印出有关的信息,包括进程ID号,进程中的线程总数,然后for循环打印有关每个线程的信息。之后的for循环创建并启动4个waster线程。我在这里启动了4个线程,因为这是该系统中处理器数量的全部。在启动这些线程后,程序再次打印出改进程相关信息。
可以看到操作系统为此进程分配了ID号62457,并且当该进程最初启动时,只有一个主要执行线程。然后,在启动4个CPU浪费线程后,进程ID是不变的,但现在程序总共有5个线程。当Python创建了每个线程时,操作系统给它们分配线程号,以及唯一的线程标识符号。
$ python multi-threads.py
Process ID: 62457
Thread Count: 1
<_MainThread(MainThread, started 4492457408)>
Process ID: 62457
Thread Count: 5
<_MainThread(MainThread, started 4492457408)>
<Thread(Thread-1, started 123145390870528)>
<Thread(Thread-2, started 123145396125696)>
<Thread(Thread-3, started 123145401380864)>
<Thread(Thread-4, started 123145406636032)>
现在,如果切换到任务管理器,可以看到与运行程序之前相比,整体CPU使用率有所增加。但总的来说,考虑到我有4个线程正在运行,利用率依然非常低。这是因为Python的全局解释器锁只允许其中一个线程在任何给定时刻实际执行。所以最多这个程序只能使用一个CPU资源。
查看进程选项卡,看到一个名为Python3.6的进程,它与之前显示的相同进程ID号。同时线程列中有5个线程。在平均CPU列中这个处理使用了大概25%的CPU,这对应于使用计算机中4个逻辑处理器中的一个。