Python多线程实例教程


Posted in Python onSeptember 06, 2014

本文以实例形式较为详细的讲解了Python的多线程,是Python程序设计中非常重要的知识点。分享给大家供大家参考之用。具体方法如下:

用过Python的人都会觉得Python的多线程很类似于Java的多线程机制,但是比JAVA的多线程更灵活。在早期的Python多线程实现中,采用了thread模块。例如:  

from time import ctime,sleep 
from thread import start_new_thread 
def loop1(): 
  print "enter loop1:",ctime(); 
  sleep(3); 
  print "leave loop1:",ctime(); 
 
def loop2(): 
  print "enter loop2:",ctime(); 
  sleep(5); 
  print "leave loop2:",ctime(); 
 
def main(): 
  print "main begin:",ctime(); 
  start_new_thread(loop1, ()); 
  start_new_thread(loop2,()); 
  sleep(8); 
  print "main end:",ctime(); 
 
if __name__=="__main__": 
  main();

简单介绍下这个代码块中的函数功能,sleep是线程睡眠时间,几乎等价于JAVA中的Thread.sleep(millionseconds)

start_new_thread是实例化一个线程并运行的方法,方法的第一个参数接受一个线程运行时所执行的函数对象,第二个参数是方法执行时所需要的参数,以一个元组的形式传入。  

这大概是最早期的Python多线程实现了,注意代码中的main线程里的sleep(8)。这里的睡眠时间只能比3+5大,而不能小。如果小于这个时间,那么main主线程会提前退出,导致无论其子线程是否是后台线程,都将会中断,从而抛出线程中断异常,类似于Java的ThreadInterruptException。这个致命的影响几乎就是这个模块后期被抛弃的罪魁祸首。

当然在早期的Python多线程中,你可以利用加锁的机制来避免出现这个情况。稍微改动下以上代码:

import thread; 
from time import sleep,ctime; 
from random import choice 
#The first param means the thread number 
#The second param means how long it sleep 
#The third param means the Lock 
def loop(nloop,sec,lock): 
  print "Thread ",nloop," start and will sleep ",sec; 
  sleep(sec); 
  print "Thread ",nloop," end ",sec; 
  lock.release(); 
 
def main(): 
  seconds=[4,2]; 
  locks=[]; 
  for i in range(len(seconds)) : 
    lock=thread.allocate_lock(); 
    lock.acquire(); 
    locks.append(lock); 
     
  print "main Thread begins:",ctime(); 
  for i,lock in enumerate(locks): 
    thread.start_new_thread(loop,(i,choice(seconds),lock)); 
  for lock in locks : 
    while lock.locked() :  
      pass; 
  print "main Thread ends:",ctime(); 
 
if __name__=="__main__" : 
  main();

这里对Python线程运行时加入了锁监控机制,介绍下红色字体标志的几个方法(其实红色字体中的lock实质是thread.lockType实例。

从以上介绍可以看出这个Lock类非常类似于JDK5.0中的java.util.concurrent.locks.Lock。不知道Doug Lea有没有参与这个模块的开发,只是比JAVA中的LOCK类多了一个方法locked,用于检测Lock对象是否还处于加锁的状态。

所以上一个例子的工作原理就是在启动线程的时候,给每个线程都加了一把锁,直到线程运行介绍,再释放这个锁。同时在Python的main线程中用一个while循环来不停的判断每个线程锁已释放。这个方法虽然避免了最开始的例子中人为的时间控制,但是还不方便,高效。

所以在较新的Python版本中,都推荐使用threading模块。

看下threading模块的API,有过JAVA开发经验的会发现它和java.lang.Thread类非常接近。这里需要说的一点就是threading的run方法可以返回函数值,这点在用于跟踪和判断线程运行正常与否非常有作用。

threading模块支持三种方法来创建线程。而前两种方式都与其Thread类有关。看下它的简要说明:

class Thread(_Verbose) : 
   __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None)

其中target指的是一个具体的函数,或者可调用的类实例(这里指实现了__call__方法的类实例)

第一种方法:指定线程运行的时候调用的函数。举例如下:

from time import ctime,sleep 
import threading; 
from random import choice 
 
def loop(number,sec): 
  print "Thread ",number," begins and will sleep ",sec," at ",ctime(); 
  sleep(sec); 
  print "Thread ",number,"ends at ",ctime(); 
   
def main(): 
  seconds=[2,4]; 
  threads=[]; 
  array=range(len(seconds)); 
  for i in array : 
    t=threading.Thread(target=loop,args=(i,choice(seconds))); 
    threads.append(t); 
  print "main Thread begins at ",ctime(); 
  for t in threads : 
    t.start(); 
  for t in threads : 
    t.join();     
  print "main Thread ends at ",ctime(); 
 
if __name__=="__main__" : 
  main();

这里target指向了一个具体的函数对象,而args传入了该方法调用时所必须的参数。这里传入了一个随即的睡眠时间。其中thread.join表示要等待该线程终止,和java中的Thread.join(long millionseconds)作用一样,如果不指定具体的时间的话,将会一直等待下去。

第二种方法就是指定一个可调用的类实例,实际上与前面一种非常的接近。如下所示:

from time import ctime,sleep 
import threading; 
from random import choice 
 
class ThreadFunc(object): 
  def __init__(self,func,args,name): 
    self.func=func; 
    self.args=args; 
    self.name=name; 
     
  def __call__(self): 
    self.func(*self.args); 
 
def loop(number,sec): 
  print "Thread ",number," begins and will sleep ",sec," at ",ctime(); 
  sleep(sec); 
  print "Thread ",number,"ends at ",ctime(); 
   
def main(): 
  seconds=[2,4]; 
  threads=[]; 
  array=range(len(seconds)); 
  for i in array : 
    t=threading.Thread(target=ThreadFunc(loop,(i,choice(seconds)),loop.__name__)); 
    threads.append(t); 
  print "main Thread begins at ",ctime(); 
  for t in threads : 
    t.start(); 
  for t in threads : 
    t.join();     
  print "main Thread ends at ",ctime(); 
 
if __name__=="__main__" : 
  main();

这里只是将target指向从一个函数对象变成了一个可调用的类实例。

重点推荐下第三种方式,用继承threading.Thread的方式来实现线程,有过Java多线程应用的朋友一定会对下面的例子非常熟悉。

from time import ctime,sleep 
import threading; 
from random import choice 
 
class MyThread(threading.Thread): 
  def __init__(self,func,args,name): 
    super(MyThread,self).__init__(); 
    self.func=func; 
    self.args=args; 
    self.name=name; 
       
  def run(self): 
    self.result=self.func(*self.args); 
 
  def getResult(self): 
    return self.result; 
   
def loop(number,sec): 
  print "Thread ",number," begins and will sleep ",sec," at ",ctime(); 
  sleep(sec); 
  print "Thread ",number,"ends at ",ctime(); 
   
def main(): 
  seconds=[2,4]; 
  threads=[]; 
  array=range(len(seconds)); 
  for i in array : 
    t=MyThread(loop,(i,choice(seconds)),loop.__name__); 
    threads.append(t); 
  print "main Thread begins at ",ctime(); 
  for t in threads : 
    t.start(); 
  for t in threads : 
    t.join();     
  print "main Thread ends at ",ctime(); 
 
if __name__=="__main__" : 
  main();

从上面可以看出MyThread继承了threading.Thread类,并在初始化方法中执行了必要的参数赋值。值得注意的是在Java类的继承中,如果不显示的指定调用父类的构造方法,那么默认将调用父类的无参构造方法。而在Python中,就不会主动去调用。所以这里需要显示的调用父类的初始化方法。

希望本文所述对大家的Python程序设计有所帮助。

Python 相关文章推荐
使用python绘制人人网好友关系图示例
Apr 01 Python
python实现定时同步本机与北京时间的方法
Mar 24 Python
Python json模块使用实例
Apr 11 Python
Python之py2exe打包工具详解
Jun 14 Python
Python元组操作实例分析【创建、赋值、更新、删除等】
Jul 24 Python
Python基于百度AI的文字识别的示例
Apr 21 Python
使用Python实现将list中的每一项的首字母大写
Jun 11 Python
在Django的View中使用asyncio的方法
Jul 12 Python
Python内置加密模块用法解析
Nov 25 Python
pytorch逐元素比较tensor大小实例
Jan 03 Python
Python print不能立即打印的解决方式
Feb 19 Python
学会Python数据可视化必须尝试这7个库
Jun 16 Python
Python Tkinter基础控件用法
Sep 03 #Python
Python Tkinter简单布局实例教程
Sep 03 #Python
python的tkinter布局之简单的聊天窗口实现方法
Sep 03 #Python
python中遍历文件的3个方法
Sep 02 #Python
python中的五种异常处理机制介绍
Sep 02 #Python
python之yield表达式学习
Sep 02 #Python
python中的hashlib和base64加密模块使用实例
Sep 02 #Python
You might like
一个简单的域名注册情况查询程序
2006/10/09 PHP
php str_pad 函数使用详解
2009/01/13 PHP
浅析PKI加密解密 OpenSSL
2013/07/01 PHP
PHP文件去掉PHP注释空格的函数分析(PHP代码压缩)
2013/07/02 PHP
CodeIgniter上传图片成功的全部过程分享
2013/08/12 PHP
PHP连接数据库实现注册页面的增删改查操作
2016/03/27 PHP
PHP AjaxForm提交图片上传并显示图片源码
2016/11/29 PHP
php策略模式简单示例分析【区别于工厂模式】
2019/09/25 PHP
javascript 动态修改样式和层叠样式表代码
2010/04/27 Javascript
js charAt的使用示例
2014/02/18 Javascript
JS实现距离上次刷新已过多少秒示例
2014/05/23 Javascript
JS判断字符串变量是否含有某个字串的实现方法
2016/06/03 Javascript
jQuery获取table行数并输出单元格内容的实现方法
2016/06/30 Javascript
canvas实现绘制吃豆鱼效果
2017/01/12 Javascript
layui弹出层效果实现代码
2017/05/19 Javascript
React-router 4 按需加载的实现方式及原理详解
2017/05/25 Javascript
AngularJS使用Filter自定义过滤器控制ng-repeat去除重复功能示例
2018/04/21 Javascript
vue项目使用微信公众号支付总结及遇到的坑
2018/10/23 Javascript
创建Vue项目以及引入Iview的方法示例
2018/12/03 Javascript
angular4中引入echarts的方法示例
2019/01/29 Javascript
vue router带参数页面刷新或回退参数消失的解决方法
2019/02/27 Javascript
[03:09]显微镜下的DOTA2第一期——带你走进华丽的DOTA2世界
2014/06/20 DOTA
Python实现以时间换空间的缓存替换算法
2016/02/19 Python
Python实现针对中文排序的方法
2017/05/09 Python
利用Python批量提取Win10锁屏壁纸实战教程
2018/03/27 Python
Python PIL读取的图像发生自动旋转的实现方法
2019/07/05 Python
keras的load_model实现加载含有参数的自定义模型
2020/06/22 Python
python爬虫beautifulsoup解析html方法
2020/12/07 Python
css3实现wifi信号逐渐增强效果实例
2017/08/09 HTML / CSS
美国在线购买内衣网站:HerRoom
2020/02/22 全球购物
Overload和Override的区别
2012/09/02 面试题
党的群众路线教育实践活动心得体会
2014/03/03 职场文书
土木工程专业本科生求职信
2014/10/01 职场文书
烈士陵园扫墓感想
2015/08/07 职场文书
分家协议书范本
2016/03/22 职场文书
浅谈克隆 JavaScript
2021/11/02 Javascript