理解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 相关文章推荐
python不带重复的全排列代码
Aug 13 Python
在RedHat系Linux上部署Python的Celery框架的教程
Apr 07 Python
在Python中使用zlib模块进行数据压缩的教程
Jun 26 Python
Python中基础的socket编程实战攻略
Jun 01 Python
python 容器总结整理
Apr 04 Python
python实现各进制转换的总结大全
Jun 18 Python
高效测试用例组织算法pairwise之Python实现方法
Jul 19 Python
Python列表与元组的异同详解
Jul 02 Python
Python画图实现同一结点多个柱状图的示例
Jul 07 Python
python中的逆序遍历实例
Dec 25 Python
教你如何使用Python下载B站视频的详细教程
Apr 29 Python
利用Python将list列表写入文件并读取的方法汇总
Mar 25 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 字符串 小常识
2009/06/05 PHP
PHP 开源框架22个简单简介
2009/08/24 PHP
phpmyadmin安装时提示:Warning: require_once(./libraries/common.inc.php)错误解决办法
2011/08/18 PHP
phpmyadmin打开很慢的解决方法
2014/04/21 PHP
PHP+JS实现大规模数据提交的方法
2015/07/02 PHP
js代码实现微博导航栏
2015/07/30 PHP
PHP使用trim函数去除字符串左右空格及特殊字符实例
2016/01/07 PHP
jQuery修改class属性和CSS样式整理
2015/01/30 Javascript
使用 JavaScript 进行函数式编程 (一) 翻译
2015/10/02 Javascript
值得分享和收藏的Bootstrap学习教程
2016/05/12 Javascript
AngularJS基础 ng-keypress 指令简单示例
2016/08/02 Javascript
详解JavaScript权威指南之对象
2016/09/27 Javascript
jQuery实现点击任意位置弹出层外关闭弹出层效果
2016/10/19 Javascript
jQuery实现简单的手风琴效果
2020/04/17 jQuery
vue组件间通信子与父详解(二)
2017/11/07 Javascript
浅谈vue中慎用style的scoped属性
2017/11/28 Javascript
微信小程序获取手机网络状态的方法【附源码下载】
2017/12/08 Javascript
vue利用v-for嵌套输出多层对象,分别输出到个表的方法
2018/09/07 Javascript
[45:50]完美世界DOTA2联赛PWL S3 CPG vs Forest 第二场 12.16
2020/12/17 DOTA
浅谈python数据类型及类型转换
2017/12/18 Python
Pycharm运行加载文本出现错误的解决方法
2019/06/27 Python
Spring Cloud Feign高级应用实例详解
2019/12/10 Python
CSS3网格的三个新特性详解
2014/04/04 HTML / CSS
美国新兴城市生活方式零售商:VILLA
2017/12/06 全球购物
阿联酋网上花店:Ferns N Petals
2018/02/14 全球购物
银行会计业务的个人自我评价
2013/11/02 职场文书
趣味运动会策划方案
2014/06/02 职场文书
甜品蛋糕店创业计划书
2014/09/21 职场文书
简单通用的简历自我评价
2014/09/21 职场文书
2015年社区中秋节活动总结
2015/03/23 职场文书
2015年百日安全活动总结
2015/03/26 职场文书
离职证明范本
2015/06/12 职场文书
2016国庆节活动宣传语
2015/11/25 职场文书
关于Python中*args和**kwargs的深入理解
2021/08/07 Python
以下牛机,你有几个
2022/04/05 无线电
python神经网络ResNet50模型
2022/05/06 Python