众所周知,受限于Python的GIL问题。真正放在CPU中运行的Python线程还是只有一个。因此所谓的多线程可以简单理解为多个任务线程,在一个CPU内核上快速切换造成的假象。这与Golang中那种真多线程(多个groutine并行运行在多个CPU内核上)还是有本质区别的。但是这并不是说Python中的多线程就没用,关键还是看使用场景:
场景一:繁重的数据汇总和计算(即CPU密集型)的任务,如数据清洗
场景二:频繁的网络访问、抓取数据(网络密集型)的任务,如网络爬虫
很明显,第一个场景CPU从头忙到尾,就没有一刻是清闲的。肯定是同时工作的CPU越多效率越高,Python的假象就不好使了。第二个场景虽然网络访问很频繁,但是抓取回来的数据确并不难处理,即不会对cpu形成太多工作量,主要消耗的是等待的时间。这时候Python的多线程就效果很明显。
Python中自带了一个threading模块,实现多线程运行程序。
import threading
import time
loop_count = 5
def count_num(num):
for x in range(10):
# 随着循环次数增加等待时间
time.sleep(x)
num = num + x
return num
print('单线程开始')
start = time.time()
# 简单循环5次,count_num函数
for i in range(loop_count):
count_num(i)
end = time.time()
print('单线程结束')
ret = end - start
print('单线程耗时: {}'.format(ret))
# 定义一个多线程的任务列表
task_list = []
for i in range(loop_count):
# 实例化线程任务,把count_num函数的运行交给子线程运行
task = threading.Thread(target=count_num, args=(i,))
task_list.append(task)
print('-'*20)
print('多线程开始')
start = time.time()
for task in task_list:
# 将任务取出来,调用start()方法真正开始运行子线程
task.start()
for task in task_list:
# 顺序检测已经start的子线程是否还在运行
if task.is_alive():
# hold住主线程,等待当前存活的子线程运行结束才能继续向下运行。
task.join()
print('多线程结束')
end = time.time()
ret = end - start
print('多线程耗时: {}'.format(ret))
运行时间对比,在等待时间较多的场景下多线程可以节约大量的时间。
单线程开始
单线程结束
单线程耗时: 225.1616358757019
--------------------
多线程开始
多线程结束
多线程耗时: 45.03864336013794