文章目录
一、并行编程概念
1、非并发,即按照顺序一步步执行,单核CPU,仍无法实现真正的并发,而多核可以多个任务一起执行,真正可以实现并行运行。
2、进程每打开一次应用,即有一次实例了,如双击一个QQ,即一个进程打开了,打开多个QQ,即有多个进程实例在运行了。
3、 同一个进程下的包含多个独立执行的线程,这些线程可以共享上下文 ,即共享的空间地址,包括一些数据结构,或一些资源。不同线程之间还可以进行信息共享通信,如下图:
Python全局解释器锁(GIL)
总之: Python是解释型语言,运行环境可以是虚拟机,这个虚拟机也称为解释器主循环,当运行程序时,这个解释器主循环要求每次只能有一个主要控制线程执行,因为避免多个线程同时执行时有可能导致数据不一致与状态不同步,所以即使程序中是多线程,运行时候也是一个一个执行 。所以用这个锁来切换线程,当一个线程运行完自己的字节码程序后,然后让出控制权,进入睡眠状态,让另一个线程执行。实际开发中,程序若是计算密集型消耗CPU即用多进程实现,或者底层的东西可以用c语言写,而IO密集型程序,Python会尽可能快释放锁,所以可以用Python编写。实际上只是cPython解释器有这个锁,而其他的如JavaPython解释器即没这个锁,因此这个锁,是由Python解释器运行时候加上的,与程序本身无关。
多线程之_thread模块
总概图:
模拟一个单线程耗时的案例
import time
def worker(n):
print('函数开始于:{}'.format(time.ctime()))#可以获取当前时间
time.sleep(n)#每个子线程执行所需要时间,等待几秒
print('函数结束于:{}'.format(time.ctime())) # 可以获取当前时间
def main():
print('主函数开始于:{}'.format(time.ctime())) # 可以获取当前时间
worker(4)
worker(2)
print('主函数结束于:{}'.format(time.ctime())) # 可以获取当前时间
if __name__=='__main__':
main()
测试结果:发现是一个执行完后,另一个线程再执行,即依次顺序执行的。
主函数开始于:Tue Dec 31 10:18:56 2019
函数开始于:Tue Dec 31 10:18:56 2019
函数结束于:Tue Dec 31 10:19:00 2019
函数开始于:Tue Dec 31 10:19:00 2019
函数结束于:Tue Dec 31 10:19:02 2019
主函数结束于:Tue Dec 31 10:19:02 2019
模拟多线程执行
注:不必要等一个线程执行完,你再顺序执行,而可以一起执行,先用thread模块的一些组件实现
import time
import _thread
def worker(n):
print('函数开始于:{}'.format(time.ctime()))#可以获取当前时间
time.sleep(n)#每个子线程执行所需要时间,等待几秒
print('函数结束于:{}'.format(time.ctime())) # 可以获取当前时间
def main():
print('主函数开始于:{}'.format(time.ctime())) # 可以获取当前时间
_thread.start_new_thread(worker,(4,))#开启第一个线程,参数1为执行的函数的引用,参数2为元组
_thread.start_new_thread(worker, (2,))#开启第二个线程
time.sleep(5)#让主线程等待几秒,即等待子线程执行完后再结束整个进程。
print('主函数结束于:{}'.format(time.ctime())) # 可以获取当前时间
if __name__=='__main__':
main()
测试结果:即主函数开始后,两个子线程也同时一起开始,,之后一个线程2s结束,另一个4s结束。并行各自执行各自的,并行运行了,所以总共用时4s,之后再加1s,即主线程也执行完毕了,即最后可以一起结束了。实际上main函数中 time.sleep(5)可以压缩到4s,但实际中,我们并不知道每个子任务运行时间,即休眠多长时间不知道,所以_thread模块有个锁,即在主线程运行过程中始终判断这个锁的状态,没有就等着,只有等待所有子线程执行完后,锁才释放了,主线程才结束,从而结束主进程了。所以一般不用_thread模块,麻烦
主函数开始于:Tue Dec 31 10:27:58 2019
函数开始于:Tue Dec 31 10:27:58 2019
函数开始于:Tue Dec 31 10:27:58 2019
函数结束于:Tue Dec 31 10:28:00 2019
函数结束于:Tue Dec 31 10:28:02 2019
主函数结束于:Tue Dec 31 10:28:03 2019
多线程模块之threading
第一种方式实现多线程
实例1
import time
import threading
def worker(n):
print('函数开始于:{}'.format(time.ctime()))#可以获取当前时间
time.sleep(n)#每个子线程执行所需要时间,等待几秒
print('函数结束于:{}'.format(time.ctime())) # 可以获取当前时间
def main():
print('主函数开始于:{}'.format(time.ctime())) # 可以获取当前时间
threads=[]#创建个线程列表,每建立一个线程即先不启动统一放到列表中,不想构造一个就启动一个,而是希望最后再一起启动执行。
t1=threading.Thread(target=worker,args=(4,))
threads.append(t1)
t2= threading.Thread(target=worker, args=(2,))
threads.append(t2)
for t in threads:
t.start()#依次启动每个线程,
print('主函数结束于:{}'.format(time.ctime())) # 可以获取当前时间
if __name__=='__main__':
main()
测试结果:发现确实实现了并行运行,但是发现主函数都结束了,但两个子函数最后才结束。即主线程与子线程不同步问题。
主函数开始于:Tue Dec 31 10:51:24 2019
函数开始于:Tue Dec 31 10:51:24 2019
函数开始于:Tue Dec 31 10:51:24 2019
主函数结束于:Tue Dec 31 10:51:24 2019
函数结束于:Tue Dec 31 10:51:26 2019
函数结束于:Tue Dec 31 10:51:28 2019
实例2.希望子线程结束后,再一起退出,即让主线程与子线程同步。可以用join等待
import time
import threading
def worker(n):
print('函数开始于:{}'.format(time.ctime()))#可以获取当前时间
time.sleep(n)#每个子线程执行所需要时间,等待几秒
print('函数结束于:{}'.format(time.ctime())) # 可以获取当前时间
def main():
print('主函数开始于:{}'.format(time.ctime())) # 可以获取当前时间
threads=[]#创建个线程列表,每建立一个线程即先不启动统一放到列表中,最后再一起启动执行。
t1=threading.Thread(target=worker,args=(4,))
threads.append(t1)
t2= threading.Thread(target=worker, args=(2,))
threads.append(t2)
for t in threads:
t.start()#在这依次启动每个线程
for t in threads:
t.join()#即每启动一个线程后,告诉主线程你要等我结束后你才能结束。
print('主函数结束于:{}'.format(time.ctime())) # 可以获取当前时间
if __name__=='__main__':
main()
测试结果:即主线程在子线程结束后,他才结束。也是并行运行的。
主函数开始于:Tue Dec 31 10: