当前位置: 首页 > 科技观察

Python中的多线程Threading与多进程Multiprocessing

时间:2023-03-13 13:36:59 科技观察

转载本文请联系派珀蛋巢公众号。昨天晚上,轮到我汇报小组会议的技术内容。最近在和ray和spark打交道,所以说一下并发和并行。反正大家都是管理学院出来的,很少接触这种话题,所以这个话题因为内容基础,不太可能被人笑话。本文讨论并发性和并行性。附上一段非常简单的Python代码,涉及到内置库threading[1]和multiprocessing[2]的使用。对于并发和并行,我们简单的用多线程对应并发,多进程对应并行。多线程并发更强调充分利用性能;多进程并行更强调性能上限的提升。我用一个很简单但不那么严格的类比来说明。多线程一个CPU相当于一个学生。一个学生每周开一次小组会,换句话说,每周向老师汇报一次。老师通常会同时给学生布置几项任务,比如做竞赛、做项目、看论文等。学生可能周一做竞赛,周二看论文,周三做项目……小组会上,他会做三件事老师很高兴的都报了出来,因为从老师的角度:学生正在同时做这三件事。多线程也是如此,假设你的手机只有一个单核CPU。你CPU的这0.01秒是用来播放音乐的,接下来的0.01秒是用来解析网页的……从你的角度来看:播放音乐和解析网页是同时进行的。您可以边上网边欣赏音乐。充分利用性能是什么意思?如果学生只有一份工作,他可能一周只需要花两天时间在任务上,其余时间都在钓鱼(针不搓,三点钟先喝茶!)。因此,我们采用“多线程”,让学生实现“并发”,充分发挥学生的能力。现实中,多线程、高并发等词在服务器程序中出现的频率更高。比如一个线程负责一个网络连接,一个CPU可以处理多个异步请求,大大提高了CPU利用率。多进程多CPU(multi-coreofCPU)相当于多生。一个任务可以分解为多个相互配合、同时进行的任务,这就是多进程。比如研究生课程,老师要留一个论文作业,我毕业就去,所以我不留什么重大作业。然后让我们并行进行。确定了大意之后,剩下的就写了。我们组有A、B、C、D四个同学,那么:A同学负责Introduction;学生B负责背景;学生C负责相关工作;学生D负责方法论。B同学提出异议:不应该先做Background,一个一个的嘛?兄弟,我是研究生,作业乱七八糟就差不多了。如果你被要求写,你可以写。可以预见,以上四部分是同时进行的,比一个人写四篇要快。因此,多进程并行提高了性能的上限。实际上,多处理更多地与高性能计算和分布式计算相关联。Python实现首先声明我们的实验环境。>python--versionPython3.8.5下面来定一个任务:求数的欧拉函数值。defeuler_func(n:int)->int:res=ni=2while<=n//i:ifn%i==0:res=res//i*(i-1)while(n%i==0):n=n//ii+=1ifn>1:res=res//n*(n-1)returnres求一个数的欧拉函数值可能很快,但是一堆数呢?所以我们要使用并行来完成这个Task。让我们把任务分成三个部分。task1=list(range(2,50000,3))#2,5,...task2=list(range(3,50000,3))#3,6,...task3=list(range(4,50000,3))#4,7,...defjob(task:List):fortintask:euler_func(t)我们来看看普通的serial。@timerdefnormal():job(task1)job(task2)job(task3)完成task1然后完成task2...好的,没有错。看看多线程?importthreadingasth@timerdefmutlthread():th1=th.Thread(target=job,args=(task1,))th2=th.Thread(target=job,args=(task2,))th3=th.Thread(target=job,args=(task3,))th1.start()th2.start()th3.start()th1.join()th2.join()th3.join()再看看多进程?importmultiprocessingasmp@timerdefmultcore():p1=mp.Process(target=job,args=(task1,))p2=mp.Process(target=job,args=(task2,))p3=mp.Process(target=job,args=(task3,))p1.start()p2.start()p3.start()p1.join()p2.join()p3.join()上面代码的逻辑是这样的:我创建一个线程/processwhose诞生的目的是为了完成任务job(task1)或者job(task2),job(task3),注意这里函数名和参数分开target=job,args=(task1,)然后开始(),告诉线程/进程:你可以开始工作了,他们可以做自己的工作了。我们程序的主要逻辑还得继续往下运行到join()。这里我们的意思是让线程/进程阻塞我们的主要逻辑。例如,p1.join()的意思是:如果p1没有完成它的工作,我的主要逻辑就不会继续(它是“阻塞的”)。这样,我们的函数multcore结束后,里面所有的线程/进程任务都必须完成。看一下结果:if__name__=='__main__':print("同步串行:")normal()print("多线程并发:")mutlthread()print("多进程并行:")multcore()#下面是结果同步串行:timer:using0.24116s多线程并发:timer:using0.24688s多进程并行:timer:using0.13791s结果不对,按理说,多进程并行的耗时应该是同步串行的三分之一。毕竟三个相同大小的任务是同时执行的。多线程并发比同步串行慢,因为多线程并发和同步串行的计算能力是一样的,只是多线程并发在任务之间来回切换,导致速度较慢。@timer是什么意思?哦,这是我写的装饰器,如下。deftimer(func):@wraps(func)definner_func():t=time.time()rts=func()print(f"timer:using{time.time()-t:.5f}s")返回rtsreturninner_funcno太了解“PythonModifier”的朋友,不如帮我点一下“在看”,然后跟着我,后面再详细说。