Refactor:

* Improve algorithm -- no more O(n) steps except sched.cancel().
* Improve thread safety of sched.run() and sched.empty()
  (other threads could alter the queue between the time the queue was
   first checked and when the lead event was deleted).
* Localize variable access in sched.run() to minimize overhead.
This commit is contained in:
Raymond Hettinger 2004-12-17 13:52:20 +00:00
parent 6f5b741a46
commit bf72b71630
1 changed files with 21 additions and 10 deletions

View File

@ -15,7 +15,7 @@ integers or floating point numbers, as long as it is consistent.
Events are specified by tuples (time, priority, action, argument). Events are specified by tuples (time, priority, action, argument).
As in UNIX, lower priority numbers mean higher priority; in this As in UNIX, lower priority numbers mean higher priority; in this
way the queue can be maintained fully sorted. Execution of the way the queue can be maintained as a priority queue. Execution of the
event means calling the action function, passing it the argument. event means calling the action function, passing it the argument.
Remember that in Python, multiple function arguments can be packed Remember that in Python, multiple function arguments can be packed
in a tuple. The action function may be an instance method so it in a tuple. The action function may be an instance method so it
@ -28,7 +28,7 @@ Parameterless functions or methods cannot be used, however.
# XXX instead of having to define a module or class just to hold # XXX instead of having to define a module or class just to hold
# XXX the global state of your particular time and delay functions. # XXX the global state of your particular time and delay functions.
import bisect import heapq
__all__ = ["scheduler"] __all__ = ["scheduler"]
@ -48,7 +48,7 @@ class scheduler:
""" """
event = time, priority, action, argument event = time, priority, action, argument
bisect.insort(self.queue, event) heapq.heappush(self.queue, event)
return event # The ID return event # The ID
def enter(self, delay, priority, action, argument): def enter(self, delay, priority, action, argument):
@ -68,10 +68,11 @@ class scheduler:
""" """
self.queue.remove(event) self.queue.remove(event)
heapq.heapify(self.queue)
def empty(self): def empty(self):
"""Check whether the queue is empty.""" """Check whether the queue is empty."""
return len(self.queue) == 0 return not not self.queue
def run(self): def run(self):
"""Execute events until the queue is empty. """Execute events until the queue is empty.
@ -94,13 +95,23 @@ class scheduler:
runnable. runnable.
""" """
# localize variable access to minimize overhead
# and to improve thread safety
q = self.queue q = self.queue
delayfunc = self.delayfunc
timefunc = self.timefunc
pop = heapq.heappop
while q: while q:
time, priority, action, argument = q[0] time, priority, action, argument = checked_event = q[0]
now = self.timefunc() now = timefunc()
if now < time: if now < time:
self.delayfunc(time - now) delayfunc(time - now)
else: else:
del q[0] event = pop(q)
void = action(*argument) # Verify that the event was not removed or altered
self.delayfunc(0) # Let other threads run # by another thread after we last looked at q[0].
if event is checked_event:
void = action(*argument)
delayfunc(0) # Let other threads run
else:
heapq.heappush(event)