理解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库urllib与urllib2主要区别分析
Jul 13 Python
Python实现检测服务器是否可以ping通的2种方法
Jan 01 Python
python处理大数字的方法
May 27 Python
python使用Image处理图片常用技巧分析
Jun 01 Python
基于python元祖与字典与集合的粗浅认识
Aug 23 Python
Python自定义函数定义,参数,调用代码解析
Dec 27 Python
python中字符串内置函数的用法总结
Sep 13 Python
python中字符串数组逆序排列方法总结
Jun 23 Python
使用 Python 写一个简易的抽奖程序
Dec 08 Python
使用PyTorch将文件夹下的图片分为训练集和验证集实例
Jan 08 Python
Python列表倒序输出及其效率详解
Mar 04 Python
Python descriptor(描述符)的实现
Nov 15 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语言中global和$GLOBALS[]的分析 之二
2012/02/02 PHP
php+mysqli使用面向对象方式查询数据库实例
2015/01/29 PHP
php实现短信发送代码
2015/07/05 PHP
简单的自定义php模板引擎
2016/08/26 PHP
浅析php-fpm静态和动态执行方式的比较
2016/11/09 PHP
PHP连接MySQL数据库三种实现方法
2020/12/10 PHP
基于jquery的时间段实现代码
2012/08/02 Javascript
jQuery操作DOM之获取表单控件的值
2015/01/23 Javascript
简介AngularJS的视图功能应用
2015/06/17 Javascript
基于bootstrap3和jquery的分页插件
2015/07/31 Javascript
jquery中表单 多选框的一种巧妙写法
2015/09/06 Javascript
学习JavaScript设计模式(策略模式)
2015/11/26 Javascript
Bootstrap入门书籍之(一)排版
2016/02/17 Javascript
javascript的BOM
2016/05/03 Javascript
微信小程序项目实践之主页tab选项实现
2018/07/18 Javascript
微信小程序实现弹出菜单
2018/07/19 Javascript
基于vue项目设置resolves.alias: '@'路径并适配webstorm
2020/12/02 Vue.js
python遍历 truple list dictionary的几种方法总结
2016/09/11 Python
python中类变量与成员变量的使用注意点总结
2017/04/29 Python
python检测主机的连通性并记录到文件的实例
2018/06/21 Python
python try 异常处理(史上最全)
2019/03/07 Python
python代码实现逻辑回归logistic原理
2019/08/07 Python
TensorFlow实现checkpoint文件转换为pb文件
2020/02/10 Python
Python批量将图片灰度化的实现代码
2020/04/11 Python
Keras SGD 随机梯度下降优化器参数设置方式
2020/06/19 Python
CSS3打造磨砂玻璃背景效果
2016/09/28 HTML / CSS
白俄罗斯在线大型超市:e-dostavka.by
2019/07/25 全球购物
咖啡书吧创业计划书
2014/01/13 职场文书
50岁生日感言
2014/01/23 职场文书
品质主管岗位职责
2014/03/16 职场文书
2014班子“三严三实”对照检查材料思想汇报
2014/09/18 职场文书
2015大一新生军训感言
2015/08/01 职场文书
2016年推广普通话宣传周活动总结
2016/04/06 职场文书
Mysql数据库命令大全
2021/05/26 MySQL
Python中seaborn库之countplot的数据可视化使用
2021/06/11 Python
教你用Python+selenium搭建自动化测试环境
2021/06/18 Python