首页 > 代码库 > 【python标准库学习】thread,threading(二)多线程同步
【python标准库学习】thread,threading(二)多线程同步
继上一篇介绍了python的多线程和基本用法。也说到了python中多线程中的同步锁,这篇就来看看python中的多线程同步问题。
有时候很多个线程同时对一个资源进行修改,这个时候就容易发生错误,看看这个最简单的程序:
import thread, time count = 0 def addCount(): global count for i in range(100000): count += 1 for i in range(10): thread.start_new_thread(addCount, ()) time.sleep(3) print count
创建了10个线程,每个线程对count每次增加1,共增加10万次,我们想象的结果应该是100万,但是事实真的如此吗,下面是我运行几次程序得到的结果:
每次的结果都不一样,而且都小于100万,怎么会酱紫?因为我们想象的是线程一个一个的进行count += 1的操作,就是每个线程操作count结束后另外一个线程再去操作它,可是事实却并非如此,加入刚开始的时候,线程1拿到的count是0,在修改完成前另外一个线程2也拿到了这个count,此时count为0,然后线程1修改完成将count变成1,然后线程2也操作了一次再次将count变成1,这里就相当于浪费了一次操作,而在大量的线程进行大量的这种操作的时候就会有更多的浪费,所以最后的结果就编程了这个样子。
那么要达到我们理想的状态,就只能每次让一个线程来操作count,当它操作完成后再让别的线程去操作它,那么别的线程拿到的count就是前面那个线程操作完成后的count。这个时候就需要同步锁了,多个线程只有一个同步锁,谁抢到就可以进行操作,操作完成后再释放锁让别的线程去抢。
1.thread模块
thread模块中有一个锁类就是LockType,通过thread.allocate_lock()函数来创建这个锁对象,这个锁有3个函数:
1).acquire()
获取锁,有一个布尔值的参数,设置为True则会一直阻塞到获取锁成功,而且这个阻塞是不会被打断的,设置为False的话,就会立即返回,即使没获取到锁。如果获取成功则返回True,如果锁已经被别的线程获取,就返回False。
2).release()
释放锁。
3).locked()
判断锁是否被占用。
将上面程序进行加锁操作:
import thread, time count = 0 lock = thread.allocate_lock() def addCount(): global count, lock lock.acquire() for i in range(100000): count += 1 lock.release() for i in range(10): thread.start_new_thread(addCount, ()) time.sleep(3) print count
2.threading模块
threading模块中有Lock,RLock和Condition来控制同步锁的问题,有的时候很多的线程都需要针对同一个事件作出反应,当这个事件为False的时候线程就阻塞,当事件为True的时候就执行,这个时候就需要Event了。
1).Lock和RLock
Lock和RLock的用法跟thread模块中的LockType一样,但是他们之间的区别是RLock可以被同一线程多次获取,但是Lock不可以,如果同一线程对Lock锁连续获取两次就会出现死锁的情况,因为它第二次会阻塞无法中断,而锁又无法释放,而RLock获取几次就必须要释放几次:
lock = threading.Lock() #Lock对象 lock.acquire() lock.acquire() #产生了死琐。 lock.release() lock.release() rLock = threading.RLock() #RLock对象 rLock.acquire() rLock.acquire() #在同一线程内,程序不会堵塞。 rLock.release() rLock.release()
2).Condition
同步锁只能提供最基本的同步,加入只在发生某个事件才访问一个资源,这个时候就需要条件变量Condition了。Condition不仅提供了锁的acquire和release方法,还提供了wait,notify和notifyAll方法。当线程通过acquire获得锁后因为某些资源的却是需要等待就调用wait方法进入等待状态,同时释放锁持有的锁让别的线程能够获得并执行,当资源准备充足的时候就可以调用notify通知其他线程,处于等待状态的线程被唤醒重新去acquire锁,或者wait的线程超时,因为wait有个参数timeout。Condition内部维护着一个锁对象,可以通过参数指定为Lock或者RLock,默认为RLock,还维护着一个waiting池,进入等待状态的线程就会被这个waiting池锁记录,当有线程调用了notify后,Condition就会从这个waiting池中找一个线程唤醒,通知它去acquire锁进入就绪状态等待被cpu的调度,而notifyAll就会唤醒waiting池中所有的线程通知它们去acquire锁。
最经典的条件变量的问题就是生产者消费者了,当资源为0时,消费者就wait等待生产者生产资源,当资源达到一定量时生产者就wait等待消费者来消费:
#! /usr/bin/env python # -*- coding: utf-8 -*- import threading, time class Producer(threading.Thread): run(self): for i in range(3): # 这里只循环了3次,下面消费者也只循环3次 global x print ‘producer acquire lock‘ con.acquire() print ‘producer get lock‘ if x > 0: # 当资源量大于零就等待消费者消费 print ‘x > 0, producer wait‘ con.wait() print ‘producer wake up‘ else: for i in range(5): # 一次生产5 x += 1 print ‘producing...‘ + str(x) time.sleep(1) # 每隔1秒生产1 print ‘producer notify consumer‘ con.notify() print ‘producer release lock‘ con.release() time.sleep(1) # 留更多的时间给消费者去acquire锁 class Consumer(threading.Thread): def run(self): for i in range(3): global x print ‘consumer acquire lock‘ con.acquire() print ‘consumer get lock‘ if x == 0: print ‘x = 0, consumer wait‘ con.wait() print ‘consumer wake up‘ else: for i in range(5): x -= 1 print ‘consuming...‘ + str(x) time.sleep(1) print ‘consumer notify producer‘ con.notify() print ‘consumer release lock‘ con.release() time.sleep(1) con.threading.Condition() x = 5 p = Producer() c = Consumer() p.start() c.start()
输出结果:
3).Event
有的时候多线程都等待某个事件的发生,当事件发生的时候,所有的线程就会被激活而执行。Event内置一个初始值为False的标志,调用set()就设置为True,调用clear()就设置为False,wait()方法则使所有线程处于等待状态,知道Event被设置为True。可以通过isSet方法查询Event对象的内置对象的当前状态。有点像车辆过红绿灯,灯变绿的时候就通行,灯变红就等待:
#! /usr/bin/env python # -*_ coding: utf-8 -*- import threading import random import time class VehicleThread(threading.Thread): def __init__(self, threadName, event): threading.Thread.__init__(self, name=threadName) self.threadEvent = event def run(self): time.sleep(random.randrange(1, 10)) # 睡了随即1到10秒,让车辆陆陆续续的到达 print ‘%s arrived at %s‘ % (self.getName(), time.ctime(time.time())) self.threadEvent.wait() # 调用wait,处于等待状态直到Event为True print ‘%s passed through intersection at %s‘ % (self.getName(), time.ctime(time.time())) greenLight = threading.Event() vehicleThreads = [] # 存放所有的车辆线程 for i in range(1, 11): vehicleThreads.append(VehicleThread(‘Vehicle‘ + str(i), greenLight)) for vehicle in vehicleThreads: vehicle.start() while threading.activeCount() > 1: # 因为activeCount得到所有线程的数量,包括了主线程main,所以是>1 greenLight.clear() # 将Event标示为False,标示红灯,所有线程等待 print ‘RED LIGHT! at:‘, time.ctime(time.time()) time.sleep(3) print ‘GREEN LIGHT! at:‘, time.ctime(time.time()) greenLight.set() # 睡了3秒,红灯结束,将Event标示为True,所有线程被激活 time.sleep(1)
输出结果: