-
Notifications
You must be signed in to change notification settings - Fork 2
thread
Threads are a bit tricky even on the best situations, and unlike the standard Python, Jython runs without a GIL and with real Java threads. Handling them is a bit different than perhaps expected, but it also can trivialize multiprocessing problems!
Function signature:
def async(startDelaySeconds=None, name=None, maxAllowedRuntime=None, killSwitch=None, ensureOnlyOne=False):
Decorator that elevates any function it decorates to invoke its execution in an asynchronous thread.
IFF the first argument is a str
, then that will be understood as the name
of the thread when launched.
Delay the start of the function. When the function is called, it will start a new thread and then wait startDelaySeconds
before running the function with the given arguments.
Use this when you need to kick off a process in a bit, but without blocking the current execution. It may be easy to use this to fudge around loading/race conditions, but there are many legitimate problems where waiting a few seconds drastically simplifies the async problem. It's usually better to not depend on a time, but it's handy in a pinch.
Name the thread this function gets evoked from.
In general, name your stuff. If you look on a gateway and see Thread-23
that tells you nothing. But if you see, for example, ExtraGlobal-Monitor
you know it came from ExtraGlobal. Naming things drastically simplifies debugging and makes it far, far easier to know what your threads are up to.
Plus, if you name your threads, then you can hunt them down and murder inspect them later if they get out of hand!
If a maxAllowedRuntime
is provided, then a watchdog thread is spawned to keep an eye on your function. If it takes too long, this will murder it in cold blood. Use this when you're waiting on a process you don't want to wait forever on but don't trust to finish in time. Or for a process that only needs to run for a limited amount of time.
In contrast to the max allowed runtime, the killSwitch
is a function that when returns True
will inform the watchdog to kill the thread. Sometimes you really don't know how long a thread will run for, but there is a clear signal for when it should end. The function provided should be argumentless - a simple lambda or a closure is appropriate here.
Consider the killSwitch
a variant of the sentinel
pattern, where if that signal shows up a KeyboardInterrupt
is fired (similar to a generator's StopIteration
).
When set, this prevents more than one copy of this named async function from running. When you want to be able to call this async function easily but don't want them to stack up, this will make sure that only one survives.
Note that there is a special case where if ensureOnlyOne
is negative or reversed
then it will always run the most recent invocation and kill older threads. Use this as a way to "try again". For example, a REST call may be made but new info came in that should cancel handling the previous call and repeat with the new values - simply call the function again with this set to reversed
and the old call will be killed and the new attempt invoked.
As the name implies, use this with caution: any pattern applied to it will subject all matching threads with that name to a KeyboardInterrupt
.
If you're in the habit of naming your async threads - and you should be - then this is helpful to control/kill/clean long running threads. Or threads accidentally kicked off with a while True:
in it...
NOTE: The bypass_interlock
argument is non-optional: by default it causes the function to fail. Do NOT abuse this Molly Guard, please. It is there to make sure that if you run this function, it's not on accident!
The following functions are useful for looking inside running threads or grabbing objects from them. In normal operations you'd absolutely not want to use these since they break all kinds of rules, detents, conventions, and probably some moral laws. But - on the other hand - they're super handy. For example, the Logger uses this to better understand the context it was called from (and to fill in blanks/details as needed).