Python多线程编程

Python线程编程(一)线程对象python



咱们在作软件开发的时候不少要用到多线程技术。例如若是作一个下载软件象flashget就要用到、象在线视频工具realplayer也要用到由于要同时下载media stream还要播放。其实例子是不少的。编程

线程相对进程来讲是“轻量级”的,操做系统用较少的资源建立和管理线程。程序中的线程在相同的内存空间中执行,并共享许多相同的资源。多线程

在python中如何建立一个线程对象dom

若是你要建立一个线程对象,很简单,只要你的类继承threading.Thread,而后在__init__里首先调用threading.Thread的__init__方法便可异步

import threading函数

class mythread(threading.Thread):工具

def __init__(self, threadname):ui

threading.Thread.__init__(self, name = threadname)操作系统

....线程

这才仅仅是个空线程,我可不是要他拉空车的,他可得给我干点实在活。很简单,重写类的run()方法便可,把你要在线程执行时作的事情都放到里面

import threading

import time

class mythread(threading.Thread):

def __init__(...):

....

def run(self):

for i in range(10):

print self.getName, i

time.sleep(1)

以上代码咱们让这个线程在执行以后每隔1秒输出一次信息到屏幕,10次后结束

getName()是threading.Thread类的一个方法,用来得到这个线程对象的name。还有一个方法setName()固然就是来设置这个线程对象的name的了。

若是要建立一个线程,首先就要先建立一个线程对象

mythread1 = mythread('mythread 1')

一个线程对象被建立后,他就处于“born”(诞生状态)

如何让这个线程对象开始运行呢?只要调用线程对象的start()方法便可

mythread1.start()

如今线程就处于“ready”状态或者也称为“runnable”状态。

奇怪吗?不是已经start了吗?为何不称为“running”状态呢?实际上是有缘由的。由于咱们的计算机通常是不具备真正并行处理能力的。咱们所谓的多线程只是把时间分红片断,而后隔一个时间段就让一个线程执行一下,而后进入“sleeping ”状态,而后唤醒另外一个在“sleeping”的线程,如此循环runnable->sleeping->runnable... ,只是由于计算机执行速度很快,而时间片断间隔很小,咱们感觉不到,觉得是同时进行的。因此说一个线程在start了以后只是处在了能够运行的状态,他何时运行仍是由系统来进行调度的。

那一个线程何时会“dead”呢?通常来讲当线程对象的run方法执行结束或者在执行中抛出异常的话,那么这个线程就会结束了。系统会自动对“dead”状态线程进行清理。

若是一个线程t1在执行的过程当中须要等待另外一个线程t2执行结束后才能运行的话那就能够在t1在调用t2的join()方法

....

def t1(...):

...

t2.join()

...

这样t1在执行到t2.join()语句后就会等待t2结束后才会继续运行。

可是假如t1是个死循环的话那么等待就没有意义了,那怎么办呢?能够在调用t2的join()方法的时候给一个浮点数作超时参数,这样这个线程就不会等到花儿也谢了了。我等你10s,你不回来我还不容许我改嫁啊?:)

def t1(...):

...

t2.join(10)

...

若是一个进程的主线程运行完毕而子线程还在执行的话,那么进程就不会退出,直到全部子线程结束为止,如何让主线程结束的时候其余子线程也乖乖的跟老大撤退呢?那就要把那些不听话的人设置为听话的小弟,使用线程对象的setDaemon()方法,参数为bool型。True的话就表明你要听话,我老大(主线程)扯呼,你也要跟着撤,不能拖后腿。若是是False的话就不用那么听话了,老大容许大家将在外军命有所不受的。须要注意的是setDaemon()方法必须在线程对象没有调用start()方法以前调用,不然没效果。

t1 = mythread('t1')

print t1.getName(),t1.isDaemon()

t1.setDaemon(True)

print t1.getName(),t1.isDaemon()

t1.start()

print 'main thread exit'

当执行到 print 'main thread exit' 后,主线程就退出了,固然t1这个线程也跟着结束了。可是若是不使用t1线程对象的setDaemon()方法的话,即使主线程结束了,还要等待t1线程本身结束才能退出进程。isDaemon()是用来得到一个线程对象的Daemonflag状态的。

如何来得到与线程有关的信息呢?

得到当前正在运行的线程的引用

running = threading.currentThread()

得到当前全部活动对象(即run方法开始可是未终止的任何线程)的一个列表

threadlist = threading.enumerate()

得到这个列表的长度

threadcount = threading.activeCount()

查看一个线程对象的状态调用这个线程对象的isAlive()方法,返回1表明处于“runnable”状态且没有“dead”

threadflag = threading.isAlive()






Python线程编程(二)简单的线程同步



     多个执行线程常常要共享数据,若是仅仅读取共享数据还好,可是若是多个线程要修改共享数据的话就可能出现没法预料的结果。

     假如两个线程对象t1和t2都要对数值num=0进行增1运算,那么t1和t2都各对num修改10次的话,那么num最终的结果应该为20。可是若是当t1取得num的值时(假如此时num为0),系统把t1调度为“sleeping”状态,而此时t2转换为“running”状态,此时t2得到的num的值也为0,而后他把num+1的值1赋给num。系统又把t2转化为“sleeping”状态,t1为“running”状态,因为t1已经获得num值为0,因此他也把num+1的值赋给了num为1。原本是2次增1运行,结果倒是num只增了1次。相似这样的状况在多线程同时执行的时候是有可能发生的。因此为了防止这类状况的出现就要使用线程同步机制。

     最简单的同步机制就是“锁”

     锁对象用threading.RLock类建立

    mylock = threading.RLock()

     如何使用锁来同步线程呢?线程可使用锁的acquire() (得到)方法,这样锁就进入“locked”状态。每次只有一个线程能够得到锁。若是当另外一个线程试图得到这个锁的时候,就会被系统变为“blocked”状态,直到那个拥有锁的线程调用锁的release() (释放)方法,这样锁就会进入“unlocked”状态。“blocked”状态的线程就会收到一个通知,并有权利得到锁。若是多个线程处于“blocked”状态,全部线程都会先解除“blocked”状态,而后系统选择一个线程来得到锁,其余的线程继续沉默(“blocked”)。

import threading

mylock = threading.RLock()

class mythread(threading.Thread)

     ...

     def run(self ...):

         ...     #此处 不能够 放置修改共享数据的代码

         mylock.acquire()

         ...     #此处 能够 放置修改共享数据的代码

         mylock.release()

         ...     #此处 不能够 放置修改共享数据的代码

     咱们把修改共享数据的代码称为“临界区”,必须将全部“临界区”都封闭在同一锁对象的acquire()和release()方法调用之间。

     锁只能提供最基本的同步级别。有时须要更复杂的线程同步,例如只在发生某些事件时才访问一个临界区(例如当某个数值改变时)。这就要使用“条件变量”。

     条件变量用threading.Condition类建立

    mycondition = threading.Condition()

     条件变量是如何工做的呢?首先一个线程成功得到一个条件变量后,调用此条件变量的wait()方法会致使这个线程释放这个锁,并进入“blocked”状态,直到另外一个线程调用同一个条件变量的notify()方法来唤醒那个进入“blocked”状态的线程。若是调用这个条件变量的notifyAll()方法的话就会唤醒全部的在等待的线程。

     若是程序或者线程永远处于“blocked”状态的话,就会发生死锁。因此若是使用了锁、条件变量等同步机制的话,必定要注意仔细检查,防止死锁状况的发生。对于可能产生异常的临界区要使用异常处理机制中的finally子句来保证释放锁。等待一个条件变量的线程必须用notify()方法显式的唤醒,不然就永远沉默。保证每个wait()方法调用都有一个相对应的notify()调用,固然也能够调用notifyAll()方法以防万一。






Python线程编程(三)同步队列



咱们常常会采用生产者/消费者关系的两个线程来处理一个共享缓冲区的数据。例如一个生产者线程接受用户数据放入一个共享缓冲区里,等待一个消费者线程对数 据取出处理。可是若是缓冲区的过小而生产者和消费者两个异步线程的速度不一样时,容易出现一个线程等待另外一个状况。为了尽量的缩短共享资源并以相同速度工 做的各线程的等待时间,咱们可使用一个“队列”来提供额外的缓冲区。


建立一个“队列”对象


import Queue

myqueue = Queue.Queue(maxsize = 10)


Queue.Queue类便是一个队列的同步实现。队列长度可为无限或者有限。可经过Queue的构造函数的可选参数maxsize来设定队列长度。若是maxsize小于1就表示队列长度无限。


将一个值放入队列中


myqueue.put(10)


调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为1。若是队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。若是block为0,put方法将引起Full异常。


将一个值从队列中取出


myqueue.get()


调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为1。若是队列为空且block为1,get()就使调用线程暂停,直至有项目可用。若是block为0,队列将引起Empty异常。


咱们用一个例子来展现如何使用Queue


# queue_example.py

from Queue import Queue

import threading

import random

import time

# Producer thread

class Producer(threading.Thread):

def __init__(self, threadname, queue):

threading.Thread.__init__(self, name = threadname)

self.sharedata = queue

def run(self):

for i in range(20):

print self.getName(),'adding',i,'to queue'

self.sharedata.put(i)

time.sleep(random.randrange(10)/10.0)

print self.getName(),'Finished'

# Consumer thread

class Consumer(threading.Thread):

def __init__(self, threadname, queue):

threading.Thread.__init__(self, name = threadname)

self.sharedata = queue

def run(self):

for i in range(20):

print self.getName(),'got a value:',self.sharedata.get()

time.sleep(random.randrange(10)/10.0)

print self.getName(),'Finished'

# Main thread

def main():

queue = Queue()

producer = Producer('Producer', queue)

consumer = Consumer('Consumer', queue)

print 'Starting threads ...'

producer.start()

consumer.start()

producer.join()

consumer.join()

print 'All threads have terminated.'

if __name__ == '__main__':

main()

示例代码中实现了两个类:生产者类Producer和消费者类Consumer。前者在一个随机的时间内放入一个值到队列queue中而后显示出来,后者在必定随机的时间内从队列queue中取出一个值并显示出来。

相关文章
相关标签/搜索