I'm very new to Python and multithreaded programming in general. Basically, I have a script that will copy files to another location. I would like this to be placed in another thread so I can output ....
to indicate that the script is still running.
The problem that I am having is that if the files cannot be copied it will throw an exception. This is ok if running in the main thread; however, having the following code does not work:
try:
threadClass = TheThread(param1, param2, etc.)
threadClass.start() ##### **Exception takes place here**
except:
print "Caught an exception"
In the thread class itself, I tried to re-throw the exception, but it does not work. I have seen people on here ask similar questions, but they all seem to be doing something more specific than what I am trying to do (and I don't quite understand the solutions offered). I have seen people mention the usage of sys.exc_info()
, however I do not know where or how to use it.
All help is greatly appreciated!
EDIT: The code for the thread class is below:
class TheThread(threading.Thread):
def __init__(self, sourceFolder, destFolder):
threading.Thread.__init__(self)
self.sourceFolder = sourceFolder
self.destFolder = destFolder
def run(self):
try:
shul.copytree(self.sourceFolder, self.destFolder)
except:
raise
There are a lot of really weirdly complicated answers to this question. Am I oversimplifying this, because this seems sufficient for most things to me.
If you're certain you'll only ever be running on one or the other version of Python, you could reduce the the
run()
method down to just the mangled version (if you'll only be running on versions of Python before 3), or just the clean version (if you'll only be running on versions of Python starting with 3).Example usage:
And you'll see the exception raised on the other thread when you join.
concurrent.futures.as_completed
https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed
The following solution:
Queue
Source:
Possible output:
It is unfortunately not possible to kill futures to cancel the others as one fails:
concurrent.features
; Python: concurrent.futures How to make it cancelable?threading
: Is there any way to kill a Thread?If you do something like:
then the
with
catches it, and waits for the second thread to finish before continuing. The following behaves similarly:since
future.result()
re-raises the exception if one occurred.If you want to quit the entire Python process, you might get away with
os._exit(0)
, but this likely means you need a refactor.Tested on Python 3.6.7, Ubuntu 18.04.
A simple way of catching thread's exception and communicating back to the caller method could be by passing dictionary or a list to
worker
method.Example (passing dictionary to worker method):
This was a nasty little problem, and I'd like to throw my solution in. Some other solutions I found (async.io for example) looked promising but also presented a bit of a black box. The queue / event loop approach sort of ties you to a certain implementation. The concurrent futures source code, however, is around only 1000 lines, and easy to comprehend. It allowed me to easily solve my problem: create ad-hoc worker threads without much setup, and to be able to catch exceptions in the main thread.
My solution uses the concurrent futures API and threading API. It allows you to create a worker which gives you both the thread and the future. That way, you can join the thread to wait for the result:
...or you can let the worker just send a callback when done:
...or you can loop until the event completes:
Here's the code:
...and the test function:
If an exception occurs in a thread, the best way is to re-raise it in the caller thread during
join
. You can get information about the exception currently being handled using thesys.exc_info()
function. This information can simply be stored as a property of the thread object untiljoin
is called, at which point it can be re-raised.Note that a
Queue.Queue
(as suggested in other answers) is not necessary in this simple case where the thread throws at most 1 exception and completes right after throwing an exception. We avoid race conditions by simply waiting for the thread to complete.For example, extend
ExcThread
(below), overridingexcRun
(instead ofrun
).Python 2.x:
Python 3.x:
The 3 argument form for
raise
is gone in Python 3, so change the last line to:Although it is not possible to directly catch an exception thrown in a different thread, here's a code to quite transparently obtain something very close to this functionality. Your child thread must subclass the
ExThread
class instead ofthreading.Thread
and the parent thread must call thechild_thread.join_with_exception()
method instead ofchild_thread.join()
when waiting for the thread to finish its job.Technical details of this implementation: when the child thread throws an exception, it is passed to the parent through a
Queue
and thrown again in the parent thread. Notice that there's no busy waiting in this approach .