在计算机中,一个可执行程序在运行时会创建进程,而每个进程内部可以有多个线程同时运行。一般操作系统会给每个进程划分一个内存空间,这个内存空间在多个线程间共享。那线程是如何运行的呢?本节就来剖析一下。
P 1、多线程
Python的多线程编程,由原生threading包支持。也就是说,我们使用多线程必须提前导入该包。现在,让我们来看一下它的编程逻辑。
通过上图可以看到,要启动一个新线程。首先要有一个任务函数来完成目标任务;其次是创建线程,并传递相关参数给任务函数;最后,启动线程,等待线程结束。当然,这个结构图中忽略了其它参数,如deamon参数决定启动的线程是否为守护线程,默认deamon=False,即非守护线程。关于更多参数,请参阅Python指导手册。
P 2、多线程演示
(1)必要的包导入
import timeimport threading
(2)创建任务函数
# 任务函数def myfunc(name): t0 = time.time() print(f' Thread {name} : Start') time.sleep(2) print(f' Thread {name} : End. Time={time.time()-t0}')
(3)创建多线程并运行
print('Thread test : Start')t0 = time.time()th = []for i in range(3): # 创建线程 obj = threading.Thread(target=myfunc,args=(i,)) th.append(obj)for i in range(3): # 启动线程 th[i].start()for i in range(3): # 等待线程结束,此时对线程的join()方法可以运行,但其余的代码不可执行。 th[i].join()print(f'Thread test : End . Time={time.time()-t0}')
Thread test : Start Thread 0 : Start Thread 1 : Start Thread 2 : Start Thread 0 : End. Time=2.0016415119171143 Thread 2 : End. Time=2.0036630630493164 Thread 1 : End. Time=2.0036630630493164Thread test : End. Time=2.0056276321411133
当然,也可以不等待线程结束,任由它运行,我们继续运行后续代码。但这很可能造成数据冲突,一定要慎之又慎。
print('Thread test :Start')t0 = time.time()th = []for i in range(3): # 创建线程 obj = threading.Thread(target=myfunc,args=(i,)) th.append(obj)for i in range(3): # 启动线程 th[i].start()print(f'Thread test : End . Time={time.time()-t0}')
Thread test : Start Thread 0 : Start Thread 1 : Start Thread 2 : StartThread test : End .Time=0.001964092254638672 Thread 2 : End. Time=2.0011708736419678 Thread 0 : End. Time=2.002986192703247 Thread 1 : End. Time=2.002986192703247
可以看到,因为没有运行线程的join()方法,导致程序代码print(f'Thread test : End . Time={time.time()-t0}')提前运行了。这种放任不管,线程是依然在执行的,可以根据开发需要决定是否等待线程结束。
上述两个运行实例可以看出,多线程的并行效果明显。但这里要注意,因为上述程序的任务函数是一个不占用CPU计算资源的程序,所以看上去它们是并行的。在Python中,CPU密集型任务会占用CPU资源,而因为GIL锁(但从Python3.14版本开始似乎会慢慢放开)的设计会导致其线程呈现出串行计算,达不到并发效果。所以多线程仅在IO密集型场景下并发效果明显。
-------------------------它是数字世界里的一把杀猪刀
却总能巧夺天工
它的世界是纯粹0、1组合
却总能创造无尽幻想
......
本公众号关注数据价值分析、编程学习,将不定期更新社会热点数据分析结果、编程技巧,分享数据分析工具、方法、学习等内容,欢迎有兴趣的小伙伴加入。