Python线程通信
Author:palydawn
Date:2012.5.23
Email:palydawn@163.com
最近用python写了一个网络爬虫,很多个线程同时抓取网页。程序里面设定的线程停止条件是下载的网页数大于一个实现确定的阈值,由于阈值常常设得比较大,有时候在程序跑了一段时间之后发现一些问题,只能暴力终止下来调试程序,这个时候会遇到一个问题,我的程序里面用BerkeleyDB来保存url的,python2.7自带的BerkeleyDB貌似很久才会将内存中的数据同步到文件中,贸然终止程序非常可能会造成数据的丢失,所以我选择了下载一定数量的网页之后手动同步一次,这又有了新的问题,暴力终止程序时可能会打断内存数据同步到文件中的过程,造成数据库文件损坏,读取数据库文件时引发异常。这种时候几个小时得到的数据就丢失了(当然也可以通过文件日志的方式记录已经下载的和将要下载的url等信息,但是我不愿意这么做,而且老是这么暴力也不是办法)。
今天试了一下python的线程通信,发现使用Event可以解决这个问题。
Python里面Event的用法很简单。使用threading.Event()可以得到event对象,Event中内置了一个标志,初始值为False。调用Event的set()方法可以将内置标志设为True,调用Event的clear()方法可以将内置标志设为False,使用isSet()可以检查内置标志的当前状态,调用Event的wait()方法会将线程阻塞,直到当前Event的状态被其他线程设置为True。
下面是一个简单的例子。(网上到处都是这种例子,不过针对我的目的,有些改动)。
'''
Created on 2012-5-23
@author: palydawn
'''
import threading
import time
class MyThread(threading.Thread):
def __init__(self, threadName, event):
threading.Thread.__init__(self, name = threadName)
self.threadEvent = event
def run(self):
print '%s is ready' % self.name
while True:
if self.threadEvent.isSet():
print '%s run' % self.name
time.sleep(0.1)
else:
print '%s stop' % self.name
break
#start work
event = threading.Event()
event.set()
for i in range(5):
t = MyThread(str(i), event)
t.start()
time.sleep(5)
order = raw_input('input order:')
if order == 'q':
event.clear()
将同一个Event对象传给5个线程,线程启动之后执行里面的run()方法,首先检查Event的内置值是否为True,是则进行操作
"print '%s run' % self.name"
否则终止循环,退出线程。在启动线程之前已经将Event的内置值设置为True了(event.set()),所以每个线程启动之后都在不停地运行。在主线程中
order = raw_input('input order:')
用于接收控制台的命令信息,当在控制台输入'q'时,通过event.clear()将event的内置标志设为False,线程检查到event内置标志状态为False,就会停止。没有使用wait()方法,因为我的程序里面用不上。程序运行结果如下。
|
由于多线程打印输出时间的不确定性,上面的打印出来的信息时间上有错乱,不过不影响结果的正确定,在控制台输入'q'之后(仔细看可以看到我确实输入了q,),所有的线程停止了运行。这个例子模拟了网络爬虫运行和停止的情况,在爬虫中只需要在检查到event内置标志为False是做好数据的同步和一些其他数据的保存工作再终止线程即可。对上面的例子稍作修改,在控制台循环等待order还可以做更多的事情,以达到对线程的精确控制。