Skip to content
Snippets Groups Projects
Commit cfac8139 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

New thing in async module - the TaskThread, a simple task queue.

parent 975d1c75
No related branches found
No related tags found
No related merge requests found
...@@ -4,12 +4,40 @@ ...@@ -4,12 +4,40 @@
# #
# Author: Paul McCarthy <pauldmccarthy@gmail.com> # Author: Paul McCarthy <pauldmccarthy@gmail.com>
# #
"""This module provides functions for running tasks asynchronously. """This module provides functions and classes for running tasks
asynchronously, either in an idle loop, or on a separate thread.
.. note:: The functions in this module are intended to be run from within a .. note:: The *idle* functions in this module are intended to be run from
``wx`` application. However, they will still work without ``wx``, within a ``wx`` application. However, they will still work without
albeit with slightly modified behaviour. ``wx``, albeit with slightly modified behaviour.
Idle tasks
----------
.. autosummary::
:nosignatures:
idle
inIdle
The :func:`idle` function is a simple way to run a task on an ``wx``
``EVT_IDLE`` event handler. This effectively performs the same job as the
:func:`run` function, but is more suitable for short tasks which do not
warrant running in a separate thread.
Thread tasks
------------
.. autosummary::
:nosignatures:
run
wait
TaskThread
The :func:`run` function simply runs a task in a separate thread. This The :func:`run` function simply runs a task in a separate thread. This
...@@ -21,15 +49,14 @@ intensitve task off the main GUI thread (preventing the GUI from locking up), ...@@ -21,15 +49,14 @@ intensitve task off the main GUI thread (preventing the GUI from locking up),
and to perform some clean up/refresh afterwards. and to perform some clean up/refresh afterwards.
The :func:`idle` function is a simple way to run a task on an ``wx``
``EVT_IDLE`` event handler. This effectively performs the same job as the
:func:`run` function, but is more suitable for short tasks which do not
warrant running in a separate thread.
The :func:`wait` function is given one or more ``Thread`` instances, and a The :func:`wait` function is given one or more ``Thread`` instances, and a
task to run. It waits until all the threads have finished, and then runs task to run. It waits until all the threads have finished, and then runs
the task (via :func:`idle`). the task (via :func:`idle`).
The :class:`TaskThread` class is a simple thread which runs a queue of tasks.
.. todo:: You could possibly use ``props.callqueue`` to drive the idle loop. .. todo:: You could possibly use ``props.callqueue`` to drive the idle loop.
""" """
...@@ -324,3 +351,116 @@ def wait(threads, task, *args, **kwargs): ...@@ -324,3 +351,116 @@ def wait(threads, task, *args, **kwargs):
else: else:
joinAll() joinAll()
return None return None
class Task(object):
"""Container object which encapsulates a task that is run by a
:class:`TaskThread`.
"""
def __init__(self, name, func, args, kwargs):
self.name = name
self.func = func
self.args = args
self.kwargs = kwargs
self.enabled = True
class TaskThread(threading.Thread):
"""The ``TaskThread`` is a simple thread which runs tasks. Tasks may be
enqueued and dequeued.
.. note::
"""
def __init__(self, *args, **kwargs):
"""Create a ``TaskThread`` """
threading.Thread.__init__(self, *args, **kwargs)
self.__q = queue.Queue()
self.__enqueued = {}
self.__stop = False
log.debug('New task thread')
def enqueue(self, name, func, *args, **kwargs):
"""Enqueue a task to be executed.
:arg name: Task name. Does not necessarily have to be a string,
but must be hashable.
:arg func: The task function.
All other arguments will be passed through to the task when it is
executed.
"""
log.debug('Enqueueing task: {} [{}]'.format(
name, getattr(func, '__name__', '<unknown>')))
t = Task(name, func, args, kwargs)
self.__enqueued[name] = t
self.__q.put(t)
def isQueued(self, name):
"""Returns ``True`` if a task with the given name is enqueued,
``False`` otherwise.
"""
return name in self.__enqueued
def dequeue(self, name):
"""Dequeues a previously enqueued task.
:arg name: The task to dequeue.
"""
task = self.__enqueued.get(name, None)
if task is not None:
log.debug('Dequeueing task: {}'.format(name))
task.enabled = False
def stop(self):
"""Stop the ``TaskThread`` after any currently running task has
completed.
"""
log.debug('Stopping task thread')
self.__stop = True
def run(self):
"""Run the ``TaskThread``. """
while True:
try:
task = self.__q.get(timeout=1)
except queue.Empty:
continue
finally:
if self.__stop:
break
self.__enqueued.pop(task.name, None)
if not task.enabled:
continue
log.debug('Running task: {} [{}]'.format(
task.name,
getattr(task.func, '__name__', '<unknown>')))
task.func(*task.args, **task.kwargs)
log.debug('Task completed: {} [{}]'.format(
task.name,
getattr(task.func, '__name__', '<unknown>')))
self.__q = None
self.__enqueued = None
log.debug('Task thread finished')
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment