python 线程同步:锁 可重入锁 信号量 状态量 事件 队列
这篇文章是来讲述python 线程的同步机制详情。我们将学习到锁 可重入锁 信号量 状态量 事件 队列
首先我们先看一个简单的例子,这个例子中使用到了线程模块但没有使用同步机制
这个类FetchUrls是一个基于线程的类,它将操作一个urls列表,然后写入一个文件
class FetchUrls(threading.Thread):
"""
Thread checking URLs.
"""
def __init__(self, urls, output):
"""
Constructor.
@param urls list of urls to check
@param output file to write urls output
"""
threading.Thread.__init__(self)
self.urls = urls
self.output = output
def run(self):
"""
Thread run method. Check URLs one by one.
"""
while self.urls:
url = self.urls.pop()
req = urllib2.Request(url)
try:
d = urllib2.urlopen(req)
except urllib2.URLError, e:
print 'URL %s failed: %s' % (url, e.reason)
self.output.write(d.read())
print 'write done by %s' % self.name
print 'URL %s fetched by %s' % (url, self.name)
通过这个主函数来调用
def main():
# list 1 of urls to fetch
urls1 = ['http://www.google.com', 'http://www.facebook.com']
# list 2 of urls to fetch
urls2 = ['http://www.yahoo.com', 'http://www.youtube.com']
f = open('output.txt', 'w+')
t1 = FetchUrls(urls1, f)
t2 = FetchUrls(urls2, f)
t1.start()
t2.start()
t1.join()
t2.join()
f.close()
if __name__ == '__main__':
main()
现在的情况是所有的线程都将同时写入文件,结果非常混乱,我们需要找到一种方式-使用一个线程来
写入文件,这样就需要用到像锁这样的同步机制
lock 锁
锁具有2个状态:locked 和 unlocked 。有2个方法要被用于操作这两个状态:acquire() 和
release(),它们具有以下规则:
如果状态是非锁定的,调用acquire()将状态改变为锁定
如果状态是锁定的,调用acquire()将阻塞直到其他线程调用release()
如果状态是非锁定的,调用release() 将会触发RuntimeError 异常
如果状态是锁定的,调用release()将会状态改变为非锁定的
为了解2个线程同时写入同一个文件的问题,我们将在FetchUrls 构造函数中加入锁lock,用它来
保护文件写入行为。
class FetchUrls(threading.Thread):
...
def __init__(self, urls, output, lock):
...
self.lock = lock
def run(self):
...
while self.urls:
...
self.lock.acquire()
print 'lock acquired by %s' % self.name
self.output.write(d.read())
print 'write done by %s' % self.name
print 'lock released by %s' % self.name
self.lock.release()
...
def main():
...
lock = threading.Lock()
...
t1 = FetchUrls(urls1, f, lock)
t2 = FetchUrls(urls2, f, lock)
...
我们来看一下python的内部原理,我使用的是 python2.6.6 linux
lock() 方法相当于 thread.allocate_lock,你可以在Lib/threading.py中找到
Lock = _allocate_lock
_allocate_lock = thread.allocate_lock
略过内部c语言实现
你也可以使用with关键字,这样Lock对象就可以当作内容控制,当程序执行到with语块时,acquire()
方法将会被调用,当with语块执行完成时 release()方法将会被调用,下面是使用with语法来实现
FetchUrls类
class FetchUrls(threading.Thread):
...
def run(self):
...
while self.urls:
...
with self.lock:
print 'lock acquired by %s' % self.name
self.output.write(d.read())
print 'write done by %s' % self.name
print 'lock released by %s' % self.name
...