理解python多线程(python多线程简明教程)


Posted in Python onJune 09, 2014

对于python 多线程的理解,我花了很长时间,搜索的大部份文章都不够通俗易懂。所以,这里力图用简单的例子,让你对多线程有个初步的认识。

单线程

在好些年前的MS-DOS时代,操作系统处理问题都是单任务的,我想做听音乐和看电影两件事儿,那么一定要先排一下顺序。

(好吧!我们不纠结在DOS时代是否有听音乐和看影的应用。^_^)

from time import ctime,sleep
def music():
    for i in range(2):
        print "I was listening to music. %s" %ctime()
        sleep(1)
def move():
    for i in range(2):
        print "I was at the movies! %s" %ctime()
        sleep(5)
if __name__ == '__main__':
    music()
    move()
    print "all over %s" %ctime()

我们先听了一首音乐,通过for循环来控制音乐的播放了两次,每首音乐播放需要1秒钟,sleep()来控制音乐播放的时长。接着我们又看了一场电影,

每一场电影需要5秒钟,因为太好看了,所以我也通过for循环看两遍。在整个休闲娱乐活动结束后,我通过

print "all over %s" %ctime()

看了一下当前时间,差不多该睡觉了。

运行结果:

>>=========================== RESTART ================================
>>> 
I was listening to music. Thu Apr 17 10:47:08 2014
I was listening to music. Thu Apr 17 10:47:09 2014
I was at the movies! Thu Apr 17 10:47:10 2014
I was at the movies! Thu Apr 17 10:47:15 2014
all over Thu Apr 17 10:47:20 2014

其实,music()和move()更应该被看作是音乐和视频播放器,至于要播放什么歌曲和视频应该由我们使用时决定。所以,我们对上面代码做了改造:

#coding=utf-8
import threading
from time import ctime,sleep
def music(func):
    for i in range(2):
        print "I was listening to %s. %s" %(func,ctime())
        sleep(1)
def move(func):
    for i in range(2):
        print "I was at the %s! %s" %(func,ctime())
        sleep(5)
if __name__ == '__main__':
    music(u'爱情买卖')
    move(u'阿凡达')
    print "all over %s" %ctime()

对music()和move()进行了传参处理。体验中国经典歌曲和欧美大片文化。

运行结果:

>>> ======================== RESTART ================================
>>> 
I was listening to 爱情买卖. Thu Apr 17 11:48:59 2014
I was listening to 爱情买卖. Thu Apr 17 11:49:00 2014
I was at the 阿凡达! Thu Apr 17 11:49:01 2014
I was at the 阿凡达! Thu Apr 17 11:49:06 2014
all over Thu Apr 17 11:49:11 2014

多线程

科技在发展,时代在进步,我们的CPU也越来越快,CPU抱怨,P大点事儿占了我一定的时间,其实我同时干多个活都没问题的;于是,操作系

统就进入了多任务时代。我们听着音乐吃着火锅的不在是梦想。

python提供了两个模块来实现多线程thread 和threading ,thread 有一些缺点,在threading 得到了弥补,为了不浪费你和时间,所以我们直

接学习threading 就可以了。

继续对上面的例子进行改造,引入threadring来同时播放音乐和视频:

#coding=utf-8
import threading
from time import ctime,sleep

def music(func):
    for i in range(2):
        print "I was listening to %s. %s" %(func,ctime())
        sleep(1)
def move(func):
    for i in range(2):
        print "I was at the %s! %s" %(func,ctime())
        sleep(5)
threads = []
t1 = threading.Thread(target=music,args=(u'爱情买卖',))
threads.append(t1)
t2 = threading.Thread(target=move,args=(u'阿凡达',))
threads.append(t2)
if __name__ == '__main__':
    for t in threads:
        t.setDaemon(True)
        t.start()
    print "all over %s" %ctime()

import threading

首先导入threading 模块,这是使用多线程的前提。

threads = []
t1 = threading.Thread(target=music,args=(u'爱情买卖',))
threads.append(t1)

创建了threads数组,创建线程t1,使用threading.Thread()方法,在这个方法中调用music方法target=music,args方法对music进行传参。 把创

建好的线程t1装到threads数组中。

接着以同样的方式创建线程t2,并把t2也装到threads数组。

for t in threads:
t.setDaemon(True)

t.start()

最后通过for循环遍历数组。(数组被装载了t1和t2两个线程)

setDaemon()

setDaemon(True)将线程声明为守护线程,必须在start() 方法调用之前设置,如果不设置为守护线程程序会被无限挂起。子线程启动后,父线

程也继续执行下去,当父线程执行完最后一条语句print "all over %s" %ctime()后,没有等待子线程,直接就退出了,同时子线程也一同结束。

start()

开始线程活动。
运行结果:

>>> ========================= RESTART ================================
>>> 
I was listening to 爱情买卖. Thu Apr 17 12:51:45 2014 I was at the 阿凡达! Thu Apr 17 12:51:45 2014  all over Thu Apr 17 12:51:45 2014

 从执行结果来看,子线程(muisc 、move )和主线程(print "all over %s" %ctime())都是同一时间启动,但由于主线程执行完结束,所以导致子线程也终止。

继续调整程序:

...
if __name__ == '__main__':
    for t in threads:
        t.setDaemon(True)
        t.start()    t.join()

    print "all over %s" %ctime()

 我们只对上面的程序加了个join()方法,用于等待线程终止。join()的作用是,在子线程完成运行之前,这个子线程的父线程将一直被阻塞。

注意:  join()方法的位置是在for循环外的,也就是说必须等待for循环里的两个进程都结束后,才去执行主进程。

运行结果:

>>> ========================= RESTART ================================
>>> 
I was listening to 爱情买卖. Thu Apr 17 13:04:11 2014  I was at the 阿凡达! Thu Apr 17 13:04:11 2014
I was listening to 爱情买卖. Thu Apr 17 13:04:12 2014
I was at the 阿凡达! Thu Apr 17 13:04:16 2014
all over Thu Apr 17 13:04:21 2014

从执行结果可看到,music 和move 是同时启动的。

开始时间4分11秒,直到调用主进程为4分22秒,总耗时为10秒。从单线程时减少了2秒,我们可以把music的sleep()的时间调整为4秒。

...
def music(func):
    for i in range(2):
        print "I was listening to %s. %s" %(func,ctime())
        sleep(4)
...

执行结果:

>>> ====================== RESTART ================================
>>> 
I was listening to 爱情买卖. Thu Apr 17 13:11:27 2014I was at the 阿凡达! Thu Apr 17 13:11:27 2014
I was listening to 爱情买卖. Thu Apr 17 13:11:31 2014
I was at the 阿凡达! Thu Apr 17 13:11:32 2014
all over Thu Apr 17 13:11:37 2014

子线程启动11分27秒,主线程运行11分37秒。

虽然music每首歌曲从1秒延长到了4 ,但通多程线的方式运行脚本,总的时间没变化。

本文从感性上让你快速理解python多线程的使用,更详细的使用请参考其它文档或资料。

 ==========================================================

class threading.Thread()说明:

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={})
This constructor should always be called with keyword arguments. Arguments are:

group should be None; reserved for future extension when a ThreadGroup class is implemented.

target is the callable object to be invoked by the run() method. Defaults to None, meaning nothing is called.

name is the thread name. By default, a unique name is constructed of the form “Thread-N” where N is a small decimal number.

args is the argument tuple for the target invocation. Defaults to ().

kwargs is a dictionary of keyword arguments for the target invocation. Defaults to {}.
If the subclass overrides the constructor, it must make sure to invoke the base class constructor (Thread.__init__()) before doing 
anything else to the thread.

之前讲了多线程的一篇博客,感觉讲的意犹未尽,其实,多线程非常有意思。因为我们在使用电脑的过程中无时无刻都在多进程和多线程。我们可以接着之前的例子继续讲。请先看我的上一篇博客。

 从上面例子中发现线程的创建是颇为麻烦的,每创建一个线程都需要创建一个tx(t1、t2、...),如果创建的线程多时候这样极其不方便。下面对通过例子进行继续改进:

player.py

#coding=utf-8
from time import sleep, ctime 
import threading
def muisc(func):
    for i in range(2):
        print 'Start playing: %s! %s' %(func,ctime())
        sleep(2)
def move(func):
    for i in range(2):
        print 'Start playing: %s! %s' %(func,ctime())
        sleep(5)
def player(name):
    r = name.split('.')[1]
    if r == 'mp3':
        muisc(name)
    else:
        if r == 'mp4':
            move(name)
        else:
            print 'error: The format is not recognized!'
list = ['爱情买卖.mp3','阿凡达.mp4']
threads = []
files = range(len(list))
#创建线程
for i in files:
    t = threading.Thread(target=player,args=(list[i],))
    threads.append(t)
if __name__ == '__main__': 
    #启动线程
    for i in files:
        threads[i].start() 
 for i in files:

 threads[i].join()
    #主线程
    print 'end:%s' %ctime()

有趣的是我们又创建了一个player()函数,这个函数用于判断播放文件的类型。如果是mp3格式的,我们将调用music()函数,如果是mp4格式的我们调用move()函数。哪果两种格式都不是那么只能告诉用户你所提供有文件我播放不了。

然后,我们创建了一个list的文件列表,注意为文件加上后缀名。然后我们用len(list) 来计算list列表有多少个文件,这是为了帮助我们确定循环次数。

接着我们通过一个for循环,把list中的文件添加到线程中数组threads[]中。接着启动threads[]线程组,最后打印结束时间。

     split()可以将一个字符串拆分成两部分,然后取其中的一部分。

>>> x = 'testing.py'
>>> s = x.split('.')[1]
>>> if s=='py':
    print s
    
py

运行结果:

Start playing: 爱情买卖.mp3! Mon Apr 21 12:48:40 2014
Start playing: 阿凡达.mp4! Mon Apr 21 12:48:40 2014
Start playing: 爱情买卖.mp3! Mon Apr 21 12:48:42 2014
Start playing: 阿凡达.mp4! Mon Apr 21 12:48:45 2014
end:Mon Apr 21 12:48:50 2014

现在向list数组中添加一个文件,程序运行时会自动为其创建一个线程。

继续改进例子:

通过上面的程序,我们发现player()用于判断文件扩展名,然后调用music()和move() ,其实,music()和move()完整工作是相同的,我们为什么不做一台超级播放器呢,不管什么文件都可以播放。经过改造,我的超级播放器诞生了。

super_player.py

#coding=utf-8
from time import sleep, ctime 
import threading
def super_player(file,time):
    for i in range(2):
        print 'Start playing: %s! %s' %(file,ctime())
        sleep(time)
#播放的文件与播放时长
list = {'爱情买卖.mp3':3,'阿凡达.mp4':5,'我和你.mp3':4}
threads = []
files = range(len(list))
#创建线程
for file,time in list.items():
    t = threading.Thread(target=super_player,args=(file,time))
    threads.append(t)
if __name__ == '__main__': 
    #启动线程
    for i in files:
        threads[i].start() 

for i in files:

    threads[i].join()
    #主线程
    print 'end:%s' %ctime()
 

首先创建字典list ,用于定义要播放的文件及时长(秒),通过字典的items()方法来循环的取file和time,取到的这两个值用于创建线程。

接着创建super_player()函数,用于接收file和time,用于确定要播放的文件及时长。

最后是线程启动运行。运行结果:

Start playing: 爱情买卖.mp3! Fri Apr 25 09:45:09 2014
Start playing: 我和你.mp3! Fri Apr 25 09:45:09 2014
Start playing: 阿凡达.mp4! Fri Apr 25 09:45:09 2014
Start playing: 爱情买卖.mp3! Fri Apr 25 09:45:12 2014
Start playing: 我和你.mp3! Fri Apr 25 09:45:13 2014
Start playing: 阿凡达.mp4! Fri Apr 25 09:45:14 2014
end:Fri Apr 25 09:45:19 2014

 

创建自己的多线程类

#coding=utf-8
import threading 
from time import sleep, ctime class MyThread(threading.Thread):

    def __init__(self,func,args,name=''):
        threading.Thread.__init__(self)
        self.name=name
        self.func=func
        self.args=args
    def run(self):
        apply(self.func,self.args)

def super_play(file,time):
    for i in range(2):
        print 'Start playing: %s! %s' %(file,ctime())
        sleep(time)

list = {'爱情买卖.mp3':3,'阿凡达.mp4':5}
#创建线程
threads = []
files = range(len(list))
for k,v in list.items():
    t = MyThread(super_play,(k,v),super_play.__name__)
    threads.append(t)        
if __name__ == '__main__': 
    #启动线程
    for i in files:
        threads[i].start() 

for i in files:

    threads[i].join()
    #主线程
    print 'end:%s' %ctime()

MyThread(threading.Thread)

创建MyThread类,用于继承threading.Thread类。

__init__()

使用类的初始化方法对func、args、name等参数进行初始化。 

apply()

apply(func [, args [, kwargs ]]) 函数用于当函数参数已经存在于一个元组或字典中时,间接地调用函数。args是一个包含将要提供给函数的按位置传递的参数的元组。如果省略了args,任何参数都不会被传递,kwargs是一个包含关键字参数的字典。

apply() 用法:

 

#不带参数的方法
>>> def say():
    print 'say in'
>>> apply(say)
say in
#函数只带元组的参数
>>> def say(a,b):
    print a,b
>>> apply(say,('hello','虫师'))
hello 虫师
#函数带关键字参数
>>> def say(a=1,b=2):
    print a,b
    
>>> def haha(**kw):
    apply(say,(),kw)
    
>>> haha(a='a',b='b')
a b

MyThread(super_play,(k,v),super_play.__name__)

由于MyThread类继承threading.Thread类,所以,我们可以使用MyThread类来创建线程。

运行结果:

Start playing: 爱情买卖.mp3! Fri Apr 25 10:36:19 2014
Start playing: 阿凡达.mp4! Fri Apr 25 10:36:19 2014
Start playing: 爱情买卖.mp3! Fri Apr 25 10:36:22 2014
Start playing: 阿凡达.mp4! Fri Apr 25 10:36:24 2014
all end: Fri Apr 25 10:36:29 2014

Python 相关文章推荐
Python3.6正式版新特性预览
Dec 15 Python
python操作MySQL 模拟简单银行转账操作
Sep 27 Python
浅谈python正则的常用方法 覆盖范围70%以上
Mar 14 Python
Python实现的从右到左字符串替换方法示例
Jul 06 Python
python保存二维数组到txt文件中的方法
Nov 15 Python
python实现倒计时小工具
Jul 29 Python
学习和使用python的13个理由
Jul 30 Python
Python解释器及PyCharm工具安装过程
Feb 26 Python
opencv 图像腐蚀和图像膨胀的实现
Jul 07 Python
用Python远程登陆服务器的步骤
Apr 16 Python
PyTorch dropout设置训练和测试模式的实现
May 27 Python
Python 数据可视化之Seaborn详解
Nov 02 Python
Python高级应用实例对比:高效计算大文件中的最长行的长度
Jun 08 #Python
Python实例分享:快速查找出被挂马的文件
Jun 08 #Python
python小技巧之批量抓取美女图片
Jun 06 #Python
Python学习笔记(二)基础语法
Jun 06 #Python
pycharm 使用心得(九)解决No Python interpreter selected的问题
Jun 06 #Python
pycharm 使用心得(八)如何调用另一文件中的函数
Jun 06 #Python
pycharm 使用心得(七)一些实用功能介绍
Jun 06 #Python
You might like
php获取根域名方法汇总
2014/10/28 PHP
CI框架给视图添加动态数据
2014/12/01 PHP
ThinkPHP函数详解之M方法和R方法
2015/09/10 PHP
js toFixed()方法的重写实现精度的统一
2014/03/06 Javascript
jQuery中click事件的定义和用法
2014/12/20 Javascript
JavaScript从数组中删除指定值元素的方法
2015/03/18 Javascript
jQuery插件multiScroll实现全屏鼠标滚动切换页面特效
2015/04/12 Javascript
echarts3 使用总结(绘制各种图表,地图)
2017/01/05 Javascript
JS简单实现获取元素的封装操作示例
2017/04/07 Javascript
jquery easyui如何实现格式化列
2017/07/30 jQuery
Vue2.0 vue-source jsonp 跨域请求
2017/08/04 Javascript
解决vue-cli3 使用子目录部署问题
2018/07/19 Javascript
JavaScript面向对象程序设计创建对象的方法分析
2018/08/13 Javascript
vue-cli3脚手架的配置及使用教程
2018/08/28 Javascript
vue中引入mxGraph的步骤详解
2019/05/17 Javascript
在vue中把含有html标签转为html渲染页面的实例
2019/10/28 Javascript
记一次用ts+vuecli4重构项目的实现
2020/05/21 Javascript
vue实现点击按钮切换背景颜色的示例代码
2020/06/23 Javascript
在Vue 中获取下拉框的文本及选项值操作
2020/08/13 Javascript
nuxt 页面路由配置,主页轮播组件开发操作
2020/11/05 Javascript
[01:11]辉夜杯战队访谈宣传片—CDEC.Y
2015/12/26 DOTA
Python异常处理总结
2014/08/15 Python
Python如何读取MySQL数据库表数据
2017/03/11 Python
使用Python如何测试InnoDB与MyISAM的读写性能
2018/09/18 Python
django使用django-apscheduler 实现定时任务的例子
2019/07/20 Python
纽约现代艺术博物馆商店:MoMA STORE(室内家具和杂货商品)
2016/08/02 全球购物
英国家庭珠宝商:T. H. Baker
2018/02/08 全球购物
会计实习期自我鉴定
2013/10/06 职场文书
葛优非诚勿扰搞笑征婚台词
2014/03/17 职场文书
车队司机个人自我鉴定
2014/04/17 职场文书
汽车维修专业自荐书
2014/05/26 职场文书
交通工程专业推荐信
2014/09/06 职场文书
教师党员自我评价范文
2015/03/04 职场文书
博士给导师的自荐信
2015/03/06 职场文书
2016年暑期教师培训心得体会
2016/01/09 职场文书
妇产科护理心得体会
2016/01/22 职场文书