Index ¦ Archives ¦ Atom

Python模块threading--多线程控制和处理


threading.Thread

Thread是threading模块中非常重要的类之一.可以使用它来创建线程.
有两种方法来创建线程:
* 一种是继承Thread类,重写它的run()方法.
* 另一种是创建一个threading.Thread对象,在它的初始化函数(_init_())中将可调用对象作为参数传入.

通过继承threading.Thread类来创建线程:
# -*- coding: utf-8 -*-
import threading
import time
import random

count = 0
class Counter(threading.Thread):
    def __init__(self,lock,threading):
        '''@summary: 初始化对象
        @param lock:所对象
        @param threadName:线程名称
        '''
        super(Counter, self).__init__(name = threadName)    # 注意:一定要显示的调用父类的初始化函数
        self.lock = lock

    def run(self):
        '''@summary:重写父类run方法,在线程启动后执行该方法内的代码.
        '''
        global count
        self.lock.acquire()
        for i in xrange(10000):
            count += 1
        self.lock.release()
        lock = threading.Lock()
        for i in range(5):
            Count(lock, "thread-" + str(i)).start()
        time.sleep(2)       # 确保线程都执行完毕
        print count

以上示例创建了一个Counter类,继承自threading.Thread.初始化函数接收两个参数,一个是锁对象lock,另一个是线程名threadName.
在Counter中,重写了继承自父类的run()方法,该方法将全局变量count从1加到10000.
接下来创建5个Counter对象,分别调用start()方法,最后打印count.
这里对run()方法和start()做以下说明:
* run():继承自Thread,run()方法在线程开启后执行,可以把相关的逻辑写到run方法中. * start():用于启动线程.

通过创建threading.Thread对象创建线程
# -*- coding: utf-8 -*-
import threading
import time
import random

count = 0
lock = threading.Lock()

def funcAdd():
    '@summary:将全局变量count从1加到10000'
    global count, lock
    lock.acquire()
    for i in xrange(10000):
        count += 1
    lock.release()

for i in range(5):
    threading.Thread(target = funcAdd, args = (), name = 'Thread-'+ str(i)).start()
time.sleep(2)       # 确保线程都执行完毕
print count

以上示例定义了一个funcAdd方法将全局变量count从1加到10000.然后把函数对象funcAdd作为参数传入Thread对象的构造函数创建5个Thread对象,再调用Thread对象的start()方法启动线程,执行funcAdd()方法.
这里着重介绍一下threading.Thread类的初始化函数原型:
def __init__(self,gruop=None,target=None,name=None,args=(),kwargs={})
* 参数group是预留的,用于将来扩展; * 参数target是一个可调用对象(也称为活动[activity]),在线程启动后执行; * 参数name是线程的名字.默认为"Thread-N",N是一个数字; * 参数args和kwargs分别标识调用target时的参数列表和关键字参数.

Thread.join([timeout])

调用Thread.join将会使主调线程堵塞,直到调用线程运行结束或超时.参数timeout是超时时间,如果未提供参数,那么主调线程将一直堵塞到被调线程结束.

threading.RLock和threading.Lock

在threading模块中定义了两种锁:threading.RLock和threading.Lock.
这两种锁的主要区别是:RLock允许在同一线程中被多次acquire.而Lock却不允许这种情况.
注意:如果使用RLock,那么acquire和release必须成对出现,调用了几次acquire就必须要调用几次release才能真正释放所有占用的锁.

threading.Condition

Condition可以理解为一种高级的锁,它提供了闭RLock和Lock更高级的功能,允许我们能够控制复杂的线程同步问题.
threading.Condition在内部维护一个锁对象(默认为RLock),可以在创建Condition对象的时候把锁对象作为参数传入.Condition也提供了acquire和release方法,其含义与锁的acquire和release方法一至,其实它只是简单的调用内部锁对象的对应的方法而已.
Condition还挺高了以下方法(特别注意:这些方法只有在占用了锁(acquier)之后才能调用,否则将会报RuntimeError异常.):
Condition.wait([timeout]):wait方法释放内部所占用的锁,同时线程被挂起,直至接收到通知被唤醒或超时(如果提供了timeout参数的话).当先城北唤醒并重新占有锁的是哈,程序才会继续执行下去. Condition.noitfy():唤醒一个挂起的线程(如果存在一个挂起的线程).注意,notify()方法不会释放所有占用的锁. Condition.notify_all() Condition.notifyAll():唤醒所有挂起的线程(如果存在挂起的线程).注意,这些方法不会释放所有占用的锁


下面写一个捉迷藏的游戏来进一步介绍threading.Condition的基本十一用.

游戏由两个人来玩,一个藏(Hider),一个找(Seeker).
游戏规则:
1. 游戏开始之后,由Seeker先把眼睛蒙上,然后通知Hider; 2. Hider接到通知后就找地方藏起来,藏好后就通知Seeker自己藏好了,可以开始找了; 3. Seeker得到通知后就开始找.

Hider和Seeker都是独立的个体,在程序中用两个独立的线程来表示,在游戏过程中,两者之间的行为有一定时序关系,我们通过Condition来控制这种时序关系.

#file hs.py
# -*- coding: utf-8 -*-
import threading
import time

#定义Hider类
class Hider(threading.Thread):
    def __init__(self, cond, name):
        super(Hider, self).__init__()
        self.cond = cond
        self.name = name

    def run(self):
        time.sleep(2)       #睡眠2秒,确保Seeker先运行,先蒙好眼睛
        self.cond.acquire()
        self.cond.notify()      # 3.Hider获取线程锁,接到Seeker蒙好眼睛的通知后调用Condition.notify()方法唤醒Seeker第一步完成后挂起的锁.然后开始藏起来
        print self.name + ':了我已经藏好了,快来找我吧!'     # 4.Hider藏好后通知Seeker自己藏好了,可以开始找了.
        self.cond.wait()        # 5.通知完后待要Congdition.wait()方法释放内部占用锁,并挂起线程.

        self.cond.notify()      # 9.Hider被找到后唤醒线程,然后抱怨被找到太快
        print self.name + ': 唉,这么快就被你找到了.  --!'
        self.cond.wait()        # 10.抱怨完后释放锁,挂起线程.

        self.cond.release()     # 12. 游戏结束,Hider释放线程锁

class Seeker(threading.Thread):
    def __init__(self,cond,name):
        super(Seeker,self).__init__()
        self.cond = cond
        self.name =  name

    def run(self):
        self.cond.acquire()
        print self.name + ': 我已经蒙好眼睛了,你快藏好,我要来找你了!'     # 1.Seeker获取线程锁,蒙好自己的眼睛并通知Hider藏起来...
        self.cond.wait()        # 2.Seeker通知完后调用Condition.wait()方法释放内部占用锁,并将线程挂起.

        self.cond.notify()      # 6.Seeker得知Hider藏好后唤醒线程开始找..
        print self.name + ':我开始找了哦...'
        time.sleep(3)
        print self.name + ': 藏哪去了?...'
        time.sleep(3)
        print self.name + ': 我找到你了哦... 哈哈哈!'    # 7.一段时间后,Seeker找到了Hider
        self.cond.wait()    # 8.Seeker释放内部占用锁,挂起线程

        self.cond.notify()
        print self.name + ': 哈哈,我赢了...!!'   # 11.最后Seeker结果线程,开心的宣布自己赢了.并最终释放线程锁.
        self.cond.release()

cond = threading.Condition()    # 创建threading.Condition对象

seeker = Seeker(cond, 'seeker')     #创建seeker玩家对象
hider = Hider(cond, 'hider')        #创建Hider玩家对象

if __name__ == '__mian__':
    seeker.start()
    hider.start()

示例结果为:

ivesein@ivesein-UbuntuPC:~$ python hs.py 
seeker: 我已经蒙好眼睛了,你快藏好!我要来找你了
hider: 我已经藏好了,快来找我吧
seeker: 我要开始找你了...
seeker: 藏哪去了?...
seeker: 我找到你了哦..哈哈哈!
hider: ,这么快就被你找到了 --!
seeker: 我赢了

threading.Timer

threading.Timer是threading.Thread的一个子类,可以在指定时间间隔后执行某个操作,下面是python手册给的一个例子:

def hello():
    print "hello world"

t = Timer(3, hello)
t.start()  # 3秒后执行hello函数.

一些零碎知识点

if name == 'main'语句

该语句块的内容只有当该文件直接执行时才会执行,当该文件是被其他问加import导入执行时是不会执行的.

© Ivesein. Built using Pelican. Theme by Giulio Fidente on github.