在前面的Blog中我已经介绍了关于让按键处理更精确,并且如何当按下一个键后延迟一段时间的处理,那么基本想法就是:在向Queue中添加按键信息的同时,增加当时的时间戳,然后在线程中循环读取这个Queue,直到没有数据为止,然后比较按键时间与当时时间,如果时间间隔不到预设值,则进行循环。当下一次循环时首先从Queue再进行读取,如果有新的按键信息则重新开始计算时间,如果没有,则继续判断是否到了预设时间,如果已经到了,则进行相应的按键的处理。那么为了在读Queue时不阻塞,我是使用了非阻塞的读取,因此需要自已来做循环。
让我们再整理一下需求:需要一个Queue,用来放按键信息,然后当用户录入时,可以不停地放到这个Queue中。有一个线程可以读这个Queue,如果Queue中有从个消息,则全部读出,然后只保留最后一个。当存在最后一个消息时,需要等待一个时间段,如果时间段到达,并且再没有新的消息时,执开始执行处理。
整个处理代码都在modules/AsyncAction程序中。上一次改进后的处理代码为(有删节,去掉了些不是重点的代码):
def __init__(self, timestep=.1, interval=0.05):
super(AsyncAction, self).__init__()
self.q = Queue.Queue(0)
self.setDaemon(True)
self.lock = thread.allocate_lock()
self.stop = False
self.running = False
self.timestep = timestep
self.last = None
self.interval = interval
def put(self, obj):
self.q.put((obj, time.time()))
def run(self):
try:
while not self.stop:
if Globals.app.wxApp.Active and not self.q.empty() and not self.running:
self.lock.acquire()
while 1:
try:
obj = self.q.get_nowait()
if obj:
self.last = obj
except:
break
self.lock.release()
if self.last:
if not self.running:
if time.time() – self.last[1] < self.timestep:
continue
self.running = True
try:
self.do_action(self.last[0])
self.last = None
except:
pass
self.running = False
time.sleep(self.interval)
except:
pass
def do_action(self, obj):
pass
从上面可以看出,在调用put放置消息时,会自动加上当时的时间,然后在线程中会对这个时间进和检查。同时对于Queue的读取是采用非阻塞方式,因此需要自行做循环。
那么我发现,如果考虑使用Queue的带超时时间的阻塞处理方式的话,可以更加简化代码,并且由于阻塞,可以减少不必要的线程的循环,应该可以对资源的占用更少,那么改进后的处理代码为:
class AsyncAction(threading.Thread):
def __init__(self, timestep=.1):
super(AsyncAction, self).__init__()
self.q = Queue.Queue(0)
self.setDaemon(True)
self.stop = False
self.timestep = timestep
self.last = None
def put(self, obj):
self.q.put(obj)
def run(self):
try:
while not self.stop:
self.last = None
while 1:
try:
obj = self.q.get(True, self.timestep)
self.last = obj
except:
if self.last:
break
if self.last:
try:
self.do_action(self.last)
self.last = None
except:
pass
except:
pass
def do_action(self, obj):
pass
从上面可以看出,put时不再计算时间了,因为真正的时间的作用是对最后一个消息有效,并且它的作为是为了延迟,所以只要在读取Queue时,采用阻塞方式,并且设置超时时间为预设值,当已经没有可读取的数据时,读取会阻塞一段时间后返回,这时就是一个合适的处理时机了。如果用户没有输入,那么线程将阻塞一段时间,然后再次阻塞。如果有输入,会立刻读出来,减少了循环处理,同时减少了循环所带来的开销。所以现在在UliPad中就是使用这种方式了。