python threading thread_python高性能编程--003--线程之thread和threading

一、线程基础

在Win32和Linux, Solaris, MacOS, BSD等大多数类Unix系统上运行时,Python支持多线程编程。Python使用POSIX兼容的线程,即pthreads。

默认情况下,源码安装的版本在2.0及以上的python;

win32的安装包中;

线程默认是打开的。

bogon:~ elaine$ python

Python 2.7.10 (default, Feb 7 2017, 00:08:15)

[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.34)] on darwin

Type "help", "copyright", "credits" or "license" for more information.

import thread

直接在命令行下import thread,如果不报错,则说明线程支持是打开的。

如果你的Python解释器在编译时,没有打开线程支持,导入模块会失败,这种情况下,你就要重新编译你的Python解释器才能使用线程。

你可以在运行配置脚本的时候,加上“-with-thread”参数。

无线程支持的情况:

onethr.py

#!/usr/bin/python

from time import sleep,ctime

def loop0():

print 'start loop 0 at:',ctime()

sleep(4)

print 'loop 0 done at:',ctime()

def loop1():

print 'start loop 1 at:',ctime()

sleep(2)

print 'loop 1 done at:',ctime()

def main():

print 'starting at:',ctime()

loop0()

loop1()

print 'all DONE at:',ctime()

if __name__ == '__main__':

main()

输出结果:

/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 /data/study/python/project/test/onethr.py

starting at: Mon Mar 26 09:21:46 2018

start loop 0 at: Mon Mar 26 09:21:46 2018

loop 0 done at: Mon Mar 26 09:21:50 2018

start loop 1 at: Mon Mar 26 09:21:50 2018

loop 1 done at: Mon Mar 26 09:21:52 2018

all DONE at: Mon Mar 26 09:21:52 2018

Process finished with exit code 0

从上述输出结果可看出,在没有线程支持的情况下,即为在单线程中顺序执行,上述代码中有两个循环,一个循环loop0结束后,另外一个循环loop1执行,整个程序的运行时间是两个循环的运行时间的总和(6s)。

其实上述代码中的两个循环是可以同时运行的,这样整个程序的运行时间为耗时最长的循环对应的运行时间。

如loop0运行时间为4s,loop1运行时间为2s,则整个程序运行时间为4s。

为了提高程序的运行效率,python中有了多线程编程。

python有几个用于多线程编程的模块:thread,threading和Queue等。

thread模块提供了基本的线程和锁支持;

threading模块出了提供了基本的线程和锁支持,还提供了更高级别,功能更强的线程管理功能;

Queue模块是一个队列数据结构,用于实现多个进程之间数据共享。

二、thread模块

在实际项目中,不建议使用thread模块,因该模块有一个致命的缺陷:

当主线程结束时,所有的线程都会被强制结束,没有任何的告警和正常的线程清除工作,这个是没办法接受的。

模块函数:

start_new_thread()

产生一个新的线程,在新线程中用指定的参数和可选的kwargs来调用这个函数。

allocate_lock()

分配一个LockType类型的锁对象。

exit()

让线程退出。

LockType类型锁对象方法:

acquire()

尝试获取锁对象。

locked()

如果获取了锁对象则返回True,否则返回False。

release()

释放锁。

#!/usr/bin/python

from time import sleep,ctime

import thread

def loop0():

print 'start loop 0 at:',ctime()

sleep(4)

print 'loop 0 done at:',ctime()

def loop1():

print 'start loop 1 at:',ctime()

sleep(2)

print 'loop 1 done at:',ctime()

def main():

print 'starting at:',ctime()

thread.start_new_thread(loop0,())

thread.start_new_thread(loop1,())

sleep(6)

print 'all DONE at:',ctime()

if __name__ == '__main__':

main()

输出结果:

/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 /data/study/python/project/test/onethr.py

starting at: Mon Mar 26 13:10:30 2018

start loop 0 at: Mon Mar 26 13:10:30 2018

start loop 1 at: Mon Mar 26 13:10:30 2018

loop 1 done at: Mon Mar 26 13:10:32 2018

loop 0 done at: Mon Mar 26 13:10:34 2018

all DONE at: Mon Mar 26 13:10:36 2018

Process finished with exit code 0

start_new_thread(loop0,())是至少需要两个参数,一个是函数,一个是传递给函数的值。

从上述输出结果可以看出,loop0和loop1是同时运行的,即为并发执行。

在主线程中有一行代码:sleep(6)

这行代码的作用是让主线程停止下来,因为如果没有该行代码,主进程在运行了两个子线程后,主线程不会等待子线程执行完成就继续往下执行,这就会导致子线程没有执行完成,主线程直接打印输出”all DONE“,启动的两个子进程就会被动退出结束,这是不可取的。

在这段代码中,我们没有让主线程停下来等待所有子线程技术后再继续运行剩下的代码。

sleep(6)作用就是一种同步机制。

应该要有一种线程管理方法,而不是用sleep这种不靠谱的同步机制。

不让主线程过早或过晚退出,引入了锁的机制。

loops=[4,2]

def loop0(nloop,nsec,lock):

print 'start loop ',nloop,'at:',ctime()

sleep(nsec)

print 'loop ',nloop,'done at:',ctime()

lock.release()

def main():

print 'starting at:',ctime()

locks = []

nloops = range(len(loops))

for i in nloops:

lock = thread.allocate_lock()

lock.acquire()

locks.append(lock)

for i in nloops:

thread.start_new_thread(loop0,(i,loops[i],locks[i]))

sleep(1)

for i in nloops:

while locks[i].locked():pass

print 'all DONE at:',ctime()

if __name__ == '__main__':

main()

输出结果:

/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 /data/study/python/project/test/onethr.py

starting at: Mon Mar 26 13:32:09 2018

start loop 0 at: Mon Mar 26 13:32:09 2018

start loop 1 at: Mon Mar 26 13:32:10 2018

loop 1 done at: Mon Mar 26 13:32:12 2018

loop 0 done at: Mon Mar 26 13:32:13 2018

all DONE at: Mon Mar 26 13:32:13 2018

Process finished with exit code 0

程序说明:

通过thread.allocate_lock()生成锁;

通过lock.acquire()获取生成的锁;

通过locks.append(lock)将生成的锁添加到locks这个列表中;

通过thread.start_new_thread()即为thread模块中的start_new_thread()函数启动子线程;

在:

for i in nloops:

while locks[i].locked():pass

这几行代码的作用是,保证了在子线程都执行完成后主线程才执行完成并退出。当子线程执行完成过程中,最后一行代码是lock.release(),即为释放锁,释放锁后,lock[i].locked()为假的:

for i in nloops:

while locks[i].locked():pass

这个循环继续执行,直到locks这个包含所有锁的列表中的锁都释放掉了,主线程才执行完成并退出。

三、threading模块

threading模块相比thread模块,最明显的优势是它支持守护线程:守护线程一般是一个等待客户请求服务器,如果没有客户提出请求,它就在那等着。整个python会在所有非守护线程退出后才结束,即为进程中没有守护线程存在时才结束。

threading模块提供了Thread类和各种非常好用的同步机制。

threading模块对象:

Thread:表示一个线程的执行对象;

Lock:锁原语对象(跟thread模块里的锁对象相同);

RLock:可重入锁对象,使单线程可以再次获得已经获得的锁(递归锁定);

Condition:条件变量对象能让个线程停下爱,等待其他线程满足某个条件,如状态的改变或值的改变;

threading的Thread类是主要的运行对象。使用Thread类可以用多种方法创建线程。

主要有三种创建线程的方法:

创建一个Thread的实例,传给它一个函数;

创建一饿Thread的实例,传给它一个可调用的类对象;

从Thread派生出一个子类,创建一个这个字类的实例。

创建一个Thread的实例,传给它一个函数的方法创建线程:

import threading

from time import sleep,ctime

loops = [4,2]

def loop(nloop,nesc):

print 'start loop ', nloop, 'at:', ctime()

sleep(nesc)

print 'loop ',nloop,'done at:',ctime()

def main():

print 'starting at:',ctime()

threads = []

nloops = range(len(loops))

for i in nloops:

t = threading.Thread(target=loop,args=(i,loops[i]))

threads.append(t)

for i in nloops:

threads[i].start()

for i in nloops:

threads[i].join()

print 'all DONE at:', ctime()

if __name__ == '__main__':

main()

运行结果:

/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 /data/study/python/project/test/onethr.py

starting at: Mon Mar 26 23:19:19 2018

start loop 0 at: Mon Mar 26 23:19:19 2018

start loop 1 at: Mon Mar 26 23:19:19 2018

loop 1 done at: Mon Mar 26 23:19:21 2018

loop 0 done at: Mon Mar 26 23:19:23 2018

all DONE at: Mon Mar 26 23:19:23 2018

Process finished with exit code 0

程序说明:

threading模块的Thread有一个join()函数,允许主线程等待线程的结束;

所有线程都创建完毕后,再统一调用start()函数启动,而不是创建一个就启动一个,并且不再需要管理一堆的锁(包括分配锁/获得锁/释放锁/检查锁等),只需要简单地对每个线程调用join()函数即可;

join()会等到线程结束活着在给了timeout参数时,等到超时为止;

只有在需要等待线程结束时才需要使用join()函数,也就是说当主线程除了等待线程结束之外,还有其他事情需要做,就不用调用join()函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值