Python多线程与多进程深度解析[高清 中文]

所需积分/C币:12 2018-12-11 15:12:04 1.74MB PDF
收藏 收藏 7
举报

该文档详细讲解了python的多线程,多进程,其中深入理解章节会让你豁然开朗,以及大量的实例可看。高清文档
Python深度解析系列,Q0灸流群:518980304 X类似爱情py X多线程py ct threading 3 import time 多结Py-5 def test fun() time. sleep 8 thread name is %s, result is %s'%(threading. current thread(). name, y)) 9 for i ange(6) hread name is MainThread, result is 0 thread name is MainThread, result is 1 thread name is MainThread, result is 4 thread name is MainThread. result is 9 hread name is MainThread. result is 16 hread name is Mainthread, result is 25 [Finished in 6.1s] 按照我们循环的写法,耗时6.秒,这个效率不用我多说了吧 X英似爱悟py coding: utf-8 多线程Py-2 port threading x threading. py import tir 多结- DI 5 def test_fun(i) 6 7 print( 'thread name is %s, result is %s'%(threading. current thread().name, y)) 9 for i in range( threading. Thread( target-test fun,args=(i,),name='第'+str(i)+个线程') print(总共%s个线程% threading. activeCount() 共7个线程 threading的 activeCount(方法获取当前总共线程的个数,包含主线程 而主线程永远只有一个,如果我们需要计算当前子线程的个数只需要-1就可 以了 threading. enumerate o方法返回当前运行中的 Thread对象列表。 x类似爱情py X多线程py 2 import threading 3 import time x threading.py 多线Py-25 def test fun(t): print( thread name is %s, result is %s'%(threading. current thread().name,y)) 9 for i in range(6): t= threading. Thread( target- test fun,args=(i,),name=·第'+str(i)+'个线程") 11 t start( 12 for item in threading enumerate (): print(item) MainThread(Main Thread, started 7952)> Thread(第θ个线程, started2772) Thread(第1个线程, started12112)> < Thread(第2个线程, started916)> Thread(第3个线程, started8872) Thread(第4个线程, started15256) Thread(第5个线程, started9936)> thread name is第0个线程, result is8 thread name is第2个线程, result is4 thread name is第1个线程, result is1 thread name is第4个线程, result is16 thread name is第3个线程,resu1 thread name is第5个线程, result is25 [Finished in 1.1s] 如果想要程序成为主线程守护线程可以设置 daemon参数,也可以用过 sctdacmon(方法,如果你能看懂源码,你会发现 sctdacmon接收的值就是 Thread类中的 daemon的参数。我们米看看 daemon的作用。 Python深度解析系列,Q0灸流群:518980304 x类似爱情py coding: utf-8 X多线程 ng 3 X threading- py def test fun (i): 7 time. sleep(1) Re for pRint(thread name is %s, result is %s%(threading. current- thread ().name,y)) 8 t= threading. Thread( target-test fun,args=(i,),name='第'+str(i)+个线程’) 11 [Finished in 0Is] 这里为什么没有打印子线程呢?这是因为我们设置了主线程为守护线程,也 即主线程单独执行,并不会去米心子线程是否执行完毕,主线程执行完毕之 后程序就直接退出了,而子线程还没有执行完就一并退出了。 Sctdacmon的 反方法就是join(。 join(方法被调用之后,被作用的线程执行完毕之后就 会判断子线程是否执行完毕,如果子线程没有执行完,就会等待子线程执行 完毕之后,一并退出,我们上图看一下。唯一不同的就是 setdaemon( False) 只是对所有了进程同吋起作用,而 join(是针对每个进程单独起作用 X多线栏py 6 ngP 多线程py-D8 time. sleep (1) 9 for print(thread name is %s, result is %s '%(threading. current_ thread().name,y)) i in range (6) t= threading. Thread( target=test fun,args=(i,),name='第'+str(i)+'个线程") tstart() 13 print("he1lo°) thread name is第0个线程, result is0 ello thread name is第1个线程, result is1 thread name is第2个线程, result is4 hello hread name is第3个线程, result is9 thread name is第4个线程, result is16 hread name is第5个线程, result is25 hello [ Finished in 6. 2s] 解释如下:第一个子进程执行的时候,出于设置了的join(),所以第一个子 进程没执行完的时候,并不会去执行下边的 print(,直到子进程结束,才 会执行 print( hello’),进而往下交替执行。这样的不好的地方就是因为 子线程没有同时执行,失去了并发的意义,所以执行时问达到了6.2秒。 如果觉得还是不理解的,大家可以看下别人的博客,我提供一篇我以前学习 的时候看过的别的大神的大作,不过要求大家对类的继承非常熟悉: http://blog.csdnnet/zhangzheng0413/article/details/41728869/ Python深度解析系列,Q0灸流群:518980304 多践 5 def 6 time. sleep(1) 多转程py print( thread name is %s, result is %s'%(threading. current thread().name, y)) 9v for i in range(6) 19 t= threading. Thread( target= test fun,args=(i,),name='第"+str(i)+个线程) t setDaemon(False) 12 hell hell hell hello thread name is第8个线程,resu1tis thread name is第4个线程 thread name is第5个线程,resu 第2个线程,resu1 thread name is第1个线程 thread name is第3个线程, result [Finished 出于设置了 sctdacmon( Falsc)主线程执行完毕后,会等待子线程,所以才会 出现了打印出了所有的 hello,才返回了子线程的结果,而后一起退出程序。 这里需要大家细细体会,总的来说, setdaemon是针对所有的了线程,而join 需要对每一个作用的线程单独负责,分工不同 C.多线程的线程锁 多线程执行的吋候,所有变量都会共享,每个线程的执行都会对变量就行更 改,这么做最大的问题就是会导致变量的更迭,获取错误的信息。我们先说 下 python中的公共变量,用 global来定义,我们平时用到的都是局部变 量,只会在函数内部调用。直接上代码。 多线程py-D15a=5 16 lock= threading Lock() 17 def get num(n) 18 global a 19 for i in range(100000): 29 a t=n 21 a 22 print(a is %s'%a 23 t1= threading Thread (target=get_ num, args=(5,)) 24 t2- threading Thread(target=get num, args=(6,)) 25 t1. start 26 t2. start 27 print(a is ais日 is 5 [Finished in 0. 2s] 大家可以看到,没有加线程锁,因为在循环次数比较大,t1和t2交替执行 的时候,在获取a的时候就会产生获取异常的a的值,当然循环次数少的时 候,并没有什么不正常。为了防止线程在同时获取a,改变a的值,从而去 影响其它的线程,我们可以使用 threading.Lock。 threading. Lock(中有 两个方法,acqμircO获取线程锁, rcasc o释放线程锁。 Python深度解析系列,Q0灸流群:518980304 x类似爱情py15a=5 多线程-d1610ck= threading,Lock( X threading. py 17 def get num(n): 18 global ●多线程py-D19 20 lock acquire 21 for i in range(100000): a +=n 23 24 finally: 25 lock release( 26 print ('a is %s% a) 27 t1= threading Thread(target=get num, args=(5,)) 28 t2= threading Thread (target=get num, args=(6,)) 29 t1 start() 30 t2. start 31 print("ais%s'‰a) a is 5 a is 5 is 5 [Finished in 0.2s] 使用线程锁的时候要注意一定要加上释放线程锁,不然别的线程拿到不到这 个锁,就会导致线程永远等待下去。所以可以使用try-- finally,释放线 程锁,让别的线程有机会获取 D.多线程的并发控制 回到回头,我们说爬图片的吋候张张的爬太慢了,效率起见,我们可以 同时20张同时爬,也就是我们可以同时开启20个线程。问题米了,我们如 何控制这个线程的个数呢写20个 threading. Thread(吗?重复的动作往 往都是编程的活,因此 py thon也不例外,在处理这个重复的线程的吋候,我 们可以使用线程池来控制。当然你说用循环可以吗?可以,但是你却没法控 制线程的个数。从 Python3.2开始,标准库为我们提供了 concurrent. futures模块,它提供了 ThreadPoolexecutor和 ProccssPoolexccutor两个类,实现了对 threading和 multiprocessing的 更高级的抽象,对编写线程池/进程池提供了直接的支持。 concurrent. futures基础模块是 executor和 future。我们看看源码,在 futuro中有三个文件,bas, thread, proccss o basc是基础类,而 threadheprocess分别为对进程和线程的控制,都继承至base。 program files )Python > Lib )concurrent > futures 己搜索" futures 名称 修改日期 类型 peache 2017/3/1112:05 文件夹 E init py 2016/5/1616:43 PY文件 base. py 2016/5/1616:43 PY文件 process.py 2016/9/519:43 PY文件 E thread. py 2016/9/1122:51 Y文件 Python深度解析系列,Q0灸流群:518980304 我们今天的 ThreadPoolexecutor类,继承来至与base屮 Executor。看看源 码中 ThreadPoolexecutor 83 cLass ThreadPoolExecutor( base Executor): 84 def init (self, max workers-None, thread name prefix=): Initializes a new Thread PoolExecutor instance Args: 88 max workers: The maximum number of threads that can be used to execute the given call 99 thread_name_prefix: An optional name prefix to give our threads if max workers is None Use this number because ThreadPoolExecutor is often used to overlap I/o instead of CPU work max workers=(os cpu count( or 1)*5 if max workers < 0 97 raise ValueError("max workers must be greater than 0") self. max workers= max workers self. work queue= queue. Queueo) 101 self.threads= seto) 182 self. shutdown= False self. shutdown lock threading Lock() 184 self. thread name prefix= thread name prefix 我们看见类的实例需要一个线程池数,如果不给的话,程序会计算出cpu的核心 数,然后*5就是自动开启的线程池数。 509 cLass Executor(object): 518 This is an abstract base class for concrete asynchronous def submit(seLf, fn,*args, kwargs): Submits a callable to be executed with the given arg 514 Schedules the callable to be executed as fn(args, **kw 516 a Future instance representing the execution of the cal 517 518 Returns 519 A Future representing the given call 2 raise NotImpLementedError() 523 def map(self, fn, *iterabLes, timeout=None, chunksize=1): 千 timeout is not None: end time= timeout +time. timeo) fs= [self. submit(fn, args) for args in zip( iterables) Yield must be hidden in closure so that the futures are submi. before the first iterator value is required def result iterator(: t for future in fs f timeout is None yiel d future resulto yield future result(end time- time. time)) finall for future in fs future cancel() return result iterator() Python深度解析系列,Q0灸流群:518980304 这是父类 Executor,我们可以见看已经定义了mapO的方法,跟我们平时用到的 map(没有什么区别,血n代表·个两数,而 iterables代表·个可迭代对象,返 回一个序列。人家在日常的学习中如果有什么不懂的,也可以找到源码自行观看 知道了这两个方法的使用方法,我们现在就可以写我们自己的线程池了。 33 def get square (i): 35 print( the thread is %s% threading. current thread(.name) return 38 def maino f_1ist=[ i for i in range(1009)]#构建一个需要执行的参数 workers=20#设置线程池为29 41 with futures. ThreadPoolExecutor (max workers=workers )as executor res= executor. map(get square, f list) return res 44 if name== main 46 print(type(a)) 47 t(a)) the thread is <concurrent futures thread. ThreadPoolExecutor object at 0x00BB6290>_2 the thread is <concurrent. futures thread threadPoolExecutor obiect at 0x0OBB6290> the thread is <concurrent futures thread. ThreadpoolExecutor object at 0x00BB6290> [e,1,4,9,16,25,36,49,64,81,10,121,14,169,196,225256,289,324,361,49 1024,1089,11561225,1296,1369,144,1521,160,1681,1764,1849,1936,2025,2116 3249,3364,3481,3688,3721,3844,3969,4896,4225,4356,4489,4624,4761,4908,5841 6724,68897056,725,7396,7569,7744,7921,810,8281,8464,8649,8836,9025,9216】 这里我们的作用函数实现的效果就是对传递的参数进行平方,主函数构建线程池 为20。我们能从打印的名字中看见,每个线程的名字,由于条数过多所以没有全 部截图。而 concurrent. futuros. Thrcadpoolexccutor.map()返回的是一个生成 器。我们可以用 type o晰数看到,如果想要·个·个取得值可以用for来迭代, 想要看到全部,可以用1ist转换类型。 F.多进程 多进程与多线程类似,在 python中提供了 multiprocess,我们可以用 from multiprocessing import Process来导入。写一段试试看。 51 def get name (i) 52 print( the process is %s(%s)%(os. getpido,i) 53 1f name main 54 1=Process (target=get gs=(1,) 55 p2= Process(target=get_ name, args=(,)) 56 p1. start( 57 2. start 58 p1. join( 59 p2. join() the process is 11740(2) the process is 13648 (1) [Finished in 0.3s Process的参数与多进程的参数一样,os是 py thon的一个自带库,封装了常见 的系统调用。 getpid(获取当前了进程的的id, getppido获取当前主进程的ids start(和 join o方法与多线程中的一样,就不在赘述了 F.多进程的进程控制 多进程的控制,我们可以用 multiprocess下的poo1来控制,为了方便理解, Python深度解析系列,Q0灸流群:518980304 老规矩,我们来线看源码。 38 class Pool(object) Class which supports an async version of applying functions to arguments ap exception= True def Process(self, *args, **kwds) return seLf. ctx Process( args, **kwds) 145 def init (self, processes=None, initializer=None, initargs=O maxtasksperchi Ld=None, context=None) 149 elf. ctx- context or get contexto) self. setup queues( self. taskqueue queue Queue( self. cache= t 153 sel state= ru self. maxtasksperchild= maxtasksperchild self. initializer= initializer 156 Lf. initargs- initargs f processes is None processes= os cpu count( or 1 processes raise VaLue Error ("Number of processes must be at least 1 很明显, processes参数代表了我们要组件的进程池数,默认的是cup的个 数。而多进程的启动方法则是Po1中的 apply async(。 def apply async(self, func, args=O, kwds=0, calLback=None, error caLLback=None) Asynchronous version of apply( method if self. state ! RUN raise ValueError("Pool not running") result= ApplyResult(self._cache, callback, error_callback) self. taskqueue put (([(result. job, None, func, args, kwds )] None)) return result func显而易见是我们要启动的函数名,args(和kWds}为函数的可变参数 和关键字参数。至于 callback和 error callback那就是回滚和错误回滚的 参数,我们暂时不管。写一个试试。 52 def get name (i) 53 print( the process is %s(%s)'%(os getpid O,i)) print('myparent is %s(%s)'%(os. getppido,i)) name maln p=Poo1(19) 58 for i in range(10): y async(func=g , args=(i, ) p join( the process is 12560(0) myparent is 4216(0) the process is 12560(2) myparent is 4216(2) the process is 12560(4) myparent is 4216 (4) the process is 12560(6) myparent is 4216(6) 尤其要说一下 apply async o的函数参数是func,不要写成 target,否则要 Python深度解析系列,Q0灸流群:518980304 报错,从打印结果我们可以到,所有的子进程都有一个相同的父进程 c1oseO是为了结束所有的了进程的添加。 join(是针对当前进程使之成为 守护进程,只有等到其它所有的进程运行结束,才会退出程序。当然除了用 Pol我们还可以使用 Processpoolexecutor。如果你的 python是3.2以前 的,需要自行安装。 cLass ProcessPoolExecutor( base Executor) def init Lf, max workers=None): nitializes a new processpoolExecutor instance A max workers: The maximum number of processes that can be used to execute the given calls. If None or not given then as many worker processes will be created as the machine has processors check system limits() if max workers is None self. max workers= os cpu count( or 1 if max workers < 0. raise Value Error("max workers must be greater than 0") self. max workers= max workers 我们看到源码巾,参数简单粗暴,只有个进程池数量。跟上边讲的多线程 的控制使用方法基本一样,他们都继承的是base. Executor。由于上边讲了, 这里就不在细细讲了,直接写代码。 X多线程py def get name (i) print( the process is %s(%s )%(os getpid(),i) #print('myparent is %s(%s)%(os getppid(, 1)) x process.py x pool py test_list=[i i for i in range(100000)] with futures. ProcessPoolExecutor (max workers-max workers) as excutor result= excitor. map(get name, test list) print(type (result)) 63 print(list(result)) the process is 10140(9983806561) the process is 10140 (9987803721) the process is 10140(9991801681) the process is 10140 (9995800441) the process is 10140 (9999800001) <class itertools. chain'> [1,2,5,18,17,26,37,58,65,82,101,122,145,178,197,226,257,290,325,362,401, 1825,1898,1157,1226,1297,1379,1445 2,1681,1682,1765,1858,1937,2926,2117,22 3259,3365,3482,3681,3722,3845,3978, 6,4357,4498,4625,4762,4981,5842,51 准一不同的就是返回的结果是一个可迭代对象的串接,至于为什么在多线程 屮是生成器而在多进程屮是可迭代对象串接,这个问题我也不懂。后续如果有了 新的知识,我再给大家做补充。 小结:今天的内容到这就结束了,在多线程和多进程中,还有许多知识未能涉及。为什 么说在φu中多线裎只是一个羞丽的梦?多进的勺布式又是如侕设置的?协程的用 法意义究竟是什么?这些问题,我们后续继续酐究。由亍砰人也不是科班出生,所学知 识也只是从倮啤得来,如果有什么书写错误或者理解错俣,还望措教。如果你有什么更 好的意见蚁者建议请联系本人。⑩Q:383750993。00交流群:518980304。

...展开详情
试读 10P Python多线程与多进程深度解析[高清 中文]
立即下载 低至0.43元/次 身份认证VIP会员低至7折
    一个资源只可评论一次,评论内容不能少于5个字
    Janton Wang 一共10页,11个币,开玩笑!!!
    2019-07-20
    回复
    • 分享小兵

      成功上传3个资源即可获取
    关注 私信 TA的资源
    上传资源赚积分,得勋章
    最新推荐
    Python多线程与多进程深度解析[高清 中文] 12积分/C币 立即下载
    1/10
    Python多线程与多进程深度解析[高清 中文]第1页
    Python多线程与多进程深度解析[高清 中文]第2页
    Python多线程与多进程深度解析[高清 中文]第3页

    试读已结束,剩余7页未读...

    12积分/C币 立即下载 >