python多线程编程:简介-创建-方法-通信 矫情吗;* 2022-06-09 01:36 112阅读 0赞 **转载于:http://blog.csdn.net/kellyseeme/article/details/51473552** **作者:KEL** **1、多线程的发展背景** 随着计算机的发展,无论是硬件还是软件都在快速的发展。 在最开始的时候,计算机都是只有一个cpu来进行指令控制和运算,程序执行的时候都是一个进程一个进程的运行,也就是顺序执行的方式,所有的进程都是排成一个队列,然后cpu取出其中的一个进程,然后运行。 在硬件发展的时候,慢慢发展为几颗cpu,并且发展出来了几核cpu,从而在一般的服务器上都是四核的,并且至少是两颗,从而在每次服务器进行处理的时候,都是进行多进程的处理,可能说一颗cpu处理一个进程的情况出现,从而在这个时候,程序也就发展了多进程多线程的概念,在一个进程中有多个线程,而一个程序又可以运行多个线程。 在多进程和多线程主要发展出来的目标,也就是提高计算的性能,从而在这里的编程就称之为多线程编程,multi threading。 **2、 [Python][]中的多线程** 在[python][Python]中,多线程是存在的,但是和其他编程中的多线程是不一样的,也就是在一个进程中存在着多个线程,但是,很遗憾的是,在python中仅仅支持一个线程的运行,也就是说,在python中的多线程依旧像是单线程的运行。 在python中存在一个锁,也就是GIL,global interpreter lock,全局解释器锁,用来控制在同一时刻只有仅仅一个线程的运行。在python中,多线程的执行方式如下: ![20160522081327785][] 看到上面这种情况,也就是在一个进程里的确是可以有多个线程,但是同一时刻还是只有一个线程的运行,那么为什么还要使用多线程呢,用一个线程不就好了? 在python中,多线程的最好的处理的问题是对I/O密集型的操作,也就是说,在计算密集型的操作的时候,是不适合使用多线程的,因为在同一时刻,还是只有一个线程使用到cpu,但是I/O是阻塞的方式,从而python的多线程还是用在I/O操作上面,例如有大量用户的输入等IO操作。 如果是计算密集型的操作,那么在python中,更适合使用多进程的方式,在python中,使用多进程的时候,没有像线程锁的这种限制。 **3、 进程和线程的区别** 进程的英文表示为process,线程的英文表示为thread。 进程消耗的资源(主要是内存)比线程需要消耗的资源要多,进程具有自己独立的数据空间;而在线程中,线程都是存在于一个进程当中,从而所有的线程都是共享资源的,线程之间的数据通信很简单,而进程间的通信需要使用到IPC,inter process comminication,可能就要使用socket进行通信,线程之间的通信,可以使用一个[数据结构][Link 1]的容器就可以进行通信,例如数据结构Queue,这个是一个先进先出的数据结构,可以用来线程之间的通信。 在运行程序的时候,[操作系统][Link 2]管理进程,进程中存在线程,其实是进程管理线程,如果具有多个线程,那么主线程就会管理子线程,在运行程序的时候,最开始就是一个进程一个主线程,主线程会管理子线程。 在操作系统中,我们可以直接查询到进程号,也就是进程的PID,可以直接杀死一个进程,但是我们不能直接杀死一个线程,查询进程的id如下所示: **\[python\]** [view plain][] [copy][view plain] 1. \[root@python 521\]\# ps -ef|grep server |grep -v grep 2. root 109811677000:28 pts/000:00:00 python server.py 在其中pid为10981,从而可以使用kill命令直接杀死进程,然而并不能查到线程的pid,也就不能杀死一个线程。 线程的结束,要么是线程自己运行结束,要么就是主线程杀死子线程。 在线程中还有一个就是守护线程,守护线程,也就是在后台运行,主线程不会等待守护线程。使用守护进程的好处就是,主线程不用理会守护线程,如果主线程退出了,那么守护线程也就自动销毁了,守护线程的好处就是主线程不需要花时间来管理。 **4、 python中的多线程模块介绍** 在python中,多线程的模块一个是thread模块,还有一个是threading模块,推荐使用threading模块,因为threading属于高级模块,进行了大量的封装,东西总是越高级越好嘛。其实主要原因是,在thread模块中,你需要自己来进行管理子线程和主线程之间同步的问题,而在threading模块中,已经进行了相关的封装,从而不需要管同步的问题。 **5、 多线程的例子** 在使用python的多线程的时候,基本步骤如下: ![20160522090236538][] 直接调用函数的多线程如下: **\[python\]** [view plain][] [copy][view plain] 1. \#!/usr/bin/env python 2. 3. **from** threading **import** Thread 4. **import** time 5. 6. **def** loop(name,seconds): 7. **print**'start loop',name,' at:',time.ctime() 8. time.sleep(1) 9. **print**'end loop',name,' at:',time.ctime() 10. 11. **if** \_\_name\_\_ == '\_\_main\_\_': 12. loops = \[2,4\] 13. nloops = range(len(loops)) 14. threads = \[\] 15. **print**'starting at :',time.ctime() 16. **for** i **in** nloops: 17. t = Thread(target=loop,args=(i,loops\[i\],)) 18. threads.append(t) 19. **for** i **in** nloops: 20. threads\[i\].start() 21. **for** i **in** nloops: 22. threads\[i\].join() 23. 24. **print**'all DONE at :',time.ctime() 在这里,主要要做的操作在loop函数中,从而创建多个线程来执行loop函数,在创建线程对象Thread的时候,直接使用的是调用函数的方法,也就是创建线程的第一种方法,直接调用我们要进行操作的函数。 第二种方法使用的是可调用的类对象: **\[python\]** [view plain][] [copy][view plain] 1. \#!/usr/bin/env python 2. 3. **from** threading **import** Thread 4. **import** time 5. 6. **def** loop(name,seconds): 7. **print**'start loop',name,' at:',time.ctime() 8. time.sleep(1) 9. **print**'end loop',name,' at:',time.ctime() 10. 11. **class** ThreadFunc(object): 12. **def** \_\_init\_\_(self,func,args,name=''): 13. self.name = name 14. self.func = func 15. self.args = args 16. 17. **def** \_\_call\_\_(self): 18. self.func(\*self.args) 19. 20. **if** \_\_name\_\_ == '\_\_main\_\_': 21. loops = \[2,4\] 22. nloops = range(len(loops)) 23. threads = \[\] 24. **print**'starting at :',time.ctime() 25. **for** i **in** nloops: 26. t = Thread(target=ThreadFunc(loop,(i,loops\[i\]),loop.\_\_name\_\_)) 27. threads.append(t) 28. **for** i **in** nloops: 29. threads\[i\].start() 30. **for** i **in** nloops: 31. threads\[i\].join() 32. 33. **print**'all DONE at :',time.ctime() 在使用可调用类对象的时候,主要就是在类的定义中,需要定义个特殊的方法\_\_call\_\_,然后在创建Thread的时候,需要使用直接使用target为可调用对象的实例化。 第三种方法主要就是子类化,代码如下: **\[python\]** [view plain][] [copy][view plain] 1. \#!/usr/bin/env python 2. 3. **from** threading **import** Thread 4. **import** time 5. 6. **def** loop(name,seconds): 7. **print**'start loop',name,' at:',time.ctime() 8. time.sleep(1) 9. **print**'end loop',name,' at:',time.ctime() 10. 11. **class** ThreadFunc(Thread): 12. **def** \_\_init\_\_(self,func,args,name=''): 13. super(ThreadFunc,self).\_\_init\_\_() 14. self.name = name 15. self.func = func 16. self.args = args 17. 18. **def** run(self): 19. self.func(\*self.args) 20. 21. **if** \_\_name\_\_ == '\_\_main\_\_': 22. loops = \[2,4\] 23. nloops = range(len(loops)) 24. threads = \[\] 25. **print**'starting at :',time.ctime() 26. **for** i **in** nloops: 27. t = ThreadFunc(loop,(i,loops\[i\]),loop.\_\_name\_\_) 28. threads.append(t) 29. **for** i **in** nloops: 30. threads\[i\].start() 31. **for** i **in** nloops: 32. threads\[i\].join() 33. 34. **print**'all DONE at :',time.ctime() 这种方法和第二种方法大同小异,主要就是在子类的时候集成Thread类,然后将可调用的方法修改为run方法即可,在Thread类对象中,如果需要继承,那么主要就是将run方法进行重写。 执行结果如下: **\[python\]** [view plain][] [copy][view plain] 1. \[root@python 522\]\# python thread\_demo.py 2. starting at : Sun May 2203:44:492016 3. start loop 0 at: Sun May 2203:44:492016 4. start loop 1 at: Sun May 2203:44:492016 5. end loop 0 at: Sun May 2203:44:502016 6. end loop 1 at: Sun May 2203:44:502016 7. all DONE at : Sun May 2203:44:502016 在一般推荐的方法中,是使用第三种方法,也就是子类化Thread类。 在这里用了三个循环,主要的目的是在进行创建的时候,不需要开启线程,从而在第二循环中,开启所有的线程。 **6、 Thread基本方法** 在threading的Thread类中,主要的方法是run方法,也就是在使用target初始化的时候关联的方法。 Thread实例的时候,提供target参数,就是需要执行的函数,另外一个args参数就是函数的参数,必须用元祖的形式提供,如果只有一个参数,必须在一个参数后加一个逗号,表示为元祖。 join表示主线程需不需要等待子线程,如果不需要,那么可以不调用join函数,在上面例子中,调用了join函数,从而主线程需要等待子线程,在使用了timeout参数之后,那么会表示主线程最多等多少秒,然后继续执行主线程,主线程结束,那么会将子线程结束掉 setDaemon是设置线程为守护线程,守护线程表示一个不重要线程,主线程结束,那么守护线程也就结束了,可以使用方法isDaemon来判断是否为守护线程 getname表示返回线程的名称,setname表示设置线程的名称,isalive表示线程是否还在运行 在设置守护线程的时候,必须在启动方法也就是start之前进行设置。 **7、 线程之间的通信** 具体的流程如下: ![20160522115941023][] 也就是生产者线程产生数据写入到共享数据中,然后消费者线程从共享数据中取出数据。 具体代码如下: **\[python\]** [view plain][] [copy][view plain] 1. \#!/usr/bin/env python 2. 3. **from** threading **import** Thread 4. **import** time 5. **from** Queue **import** Queue 6. 7. **def** writeQ(queue,i): 8. **while**True: 9. time.sleep(1) 10. **if** queue.full(): 11. time.sleep(1) 12. **else**: 13. queue.put('xxx') 14. **print**'producting object for Q..',i 15. 16. **def** readQ(queue,i): 17. **while**True: 18. time.sleep(1) 19. **if** queue.empty(): 20. time.sleep(1) 21. **print**'empty' 22. **else**: 23. val = queue.get() 24. **print**'consumed object from Q..size now:',queue.qsize(),' consumer ' ,i 25. 26. **class** Producter(Thread): 27. **def** \_\_init\_\_(self,func,args,name=''): 28. super(Producter,self).\_\_init\_\_() 29. self.name = name 30. self.args = args 31. self.func = func 32. 33. **def** run(self): 34. self.res = self.func(\*self.args) 35. \#return self.res 36. 37. **class** Consumer(Thread): 38. **def** \_\_init\_\_(self,func,args,name=''): 39. super(Consumer,self).\_\_init\_\_() 40. self.name = name 41. self.args = args 42. self.func = func 43. 44. **def** run(self): 45. self.res = self.func(\*self.args) 46. \#return self.res 47. 48. **if** \_\_name\_\_ =='\_\_main\_\_': 49. q = Queue(100) 50. **for** i **in** range(20): 51. p = Producter(writeQ,(q,'production %s ' % i)) 52. p.start() 53. **for** i **in** range(2): 54. c = Consumer(readQ,(q,'consumer %s ' % i)) 55. c.start() 在其中,主要就是使用Queue进行线程间的通信,在以上的代码中,可以进行优化,例如提取公共类,派生子类生产者和消费者。 8、线程是不是可以无限创建 一下[测试][Link 3]为测试线程是不是可以无限创建,代码如下: **\[python\]** [view plain][] [copy][view plain] 1. \[root@python 522\]\# cat thread\_test.py 2. \#!/usr/bin/env python 3. 4. **from** threading **import** Thread 5. 6. **def** testThread(): 7. **while**True: 8. **pass** 9. **while**True: 10. t = Thread(target=testThread) 11. t.start() 就是无限的创建线程。 vmstat基准线如下: ![20160522130124526][] 指标解释如下: r表示运行的队列,话说超过5,就很有压力了 in表示中断的次数 cs表示上下文切换 us表示用户cpu时间 sy表示系统的cpu时间 ![20160522130925257][] 运行一段时间后,发现,这个procs的r数量增长很快,那么表示,有多少个的线程分配到了cpu 看出来,cpu很繁忙,中断的次数和上下文切换的数量很大,而且用户的cpu时间占比比较多。 在这个时候,登录系统的时候,会发现系统非常卡,可以看出来脑子。。。CPU根本不够用了。 杀掉进程之后恢复正常。。那么去掉cpu的限制,那么可以使用如下代码: **\[python\]** [view plain][] [copy][view plain] 1. \#!/usr/bin/env python 2. 3. **from** threading **import** Thread 4. **from** time **import** sleep 5. 6. i=0 7. **def** testThread(): 8. **print** i 9. 10. **while**True: 11. i += 1 12. t = Thread(target=testThread) 13. t.start() 这次测试。。好像没有线程的限制。 人生苦短,我用python~ [Python]: http://lib.csdn.net/base/python [20160522081327785]: /images/20220609/ff71558aa32c443585e6192bf2981fe7.png [Link 1]: http://lib.csdn.net/base/datastructure [Link 2]: http://lib.csdn.net/base/operatingsystem [view plain]: http://blog.csdn.net/kellyseeme/article/details/51473552# [20160522090236538]: /images/20220609/481b1a87efa04af684674bb51d095d77.png [20160522115941023]: /images/20220609/b78b8896c00a486e9fd1af008214ed87.png [Link 3]: http://lib.csdn.net/base/softwaretest [20160522130124526]: /images/20220609/0bd8cfc1ab6749ed8cf008444b26f706.png [20160522130925257]: /images/20220609/bc049a963ba34a149ebb2daf16d5266d.png
还没有评论,来说两句吧...