4
\$\begingroup\$

I was reading on this page about using a thread, catching an exception, and notifying the calling thread.

The answer suggested Queue, but in the comments it states:

Queue is not the best vehicle to communicate an error back, unless you want to have a full queue of them. A much better construct is threading.Event()

From the Python documentation:

This is one of the simplest mechanisms for communication between threads: one thread signals an event and other threads wait for it.

I'm new to multi threading and I wanted to give it a try, so I came up with this trivial example. I'm unsure if I'm on the right track.

import threading

def dividebyzero(event,first,second):
    try:
        result = first / second
    except ZeroDivisionError:
        event.set()
        print("Can't divide by 0")
    else:
        print(result)


e = threading.Event()
t1 = threading.Thread(name='DivideByZero',target=dividebyzero, args=(e, 10, 0),)

t1.start()
t1.join()

if e.is_set():
    print("Error occurred in division thread")

Question:

  1. Using threading.Event as shown in my example, is it the correct way to signal the main thread an error as occurred?

  2. I'm not using event.wait() because it seems unnecessary, I call join() on the t1 thread, so when I call is_set() hopefully I'll get an accurate result. Is it mandatory to use event.wait() when using threading.event()

  3. I understand that if I needed to raise more exceptions using an event probably isn't ideal, how would you know which exception was raise? In that situation would a Queue be better?

If I were to use event.wait() there is a chance the main thread could hang forever, because if the exception doesn't occur the event is never set.

I don't have an error or problems with the code, it works, I just want it reviewed to make sure I used event correctly.

\$\endgroup\$

closed as off-topic by Peilonrayz, Stephen Rauch, Sᴀᴍ Onᴇᴌᴀ, Raystafarian, Mathias Ettinger May 3 '18 at 7:39

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Lacks concrete context: Code Review requires concrete code from a project, with sufficient context for reviewers to understand how that code is used. Pseudocode, stub code, hypothetical code, obfuscated code, and generic best practices are outside the scope of this site." – Peilonrayz, Stephen Rauch, Sᴀᴍ Onᴇᴌᴀ, Raystafarian, Mathias Ettinger
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 3
    \$\begingroup\$ This question seems, to me, on the verge of being hypothetical code, which is off-topic. The last question from the on-topic page is "Do I want feedback about any or all facets of the code?", which could lead to answer being pretty much "you don't need threading here, just call the function with the right parameters". Are you OK with that? \$\endgroup\$ – Mathias Ettinger May 2 '18 at 12:48
  • 1
    \$\begingroup\$ @Mathias Ettinger How is this hypothetical? dividebyzero() is a usage example, not the subject of the review. \$\endgroup\$ – Daniel May 2 '18 at 13:00
  • 1
    \$\begingroup\$ @Mathias Ettinger No, using threading.Event to signal an exception occured. If that part was a function wrapper in a separate file and wrapped dividebyzero(), there would be no issue. \$\endgroup\$ – Daniel May 2 '18 at 13:08
  • 1
    \$\begingroup\$ I agree with @MathiasEttinger here, however it's example code. Your code is just an MVCE, and so isn't something you'd use in the wild, without adapting it each time. This can be shown by getting the same output as using print('Error occurred in division thread'). \$\endgroup\$ – Peilonrayz May 2 '18 at 15:53
  • 1
    \$\begingroup\$ In any case, when you apply what you got here to a real program, I’d certainly welcome it if you create a question about it. \$\endgroup\$ – Mathias Ettinger May 3 '18 at 15:15
1
\$\begingroup\$
  1. Yes and no. Your approach works, and it is easy to understand. If, however, you want to keep track of what type of exception was raised, it could be improved. What if you have some super complex function that can raise 10 different exceptions, and you need access to the exception traceback if something goes wrong? You'd need 10 threading.Event instances. In that case, you should use a queue.Queue instead, which is thread-safe (untested snippet):

    import queue
    import threading
    
    def dividebyzero(first, second, exc_queue):
        try:
            result = first / second
        except ZeroDivisionError as exc:
            exc_queue.put(exc)
        else:
            print(result)
    
    excq = queue.Queue()
    t = threading.Thread(target=dividebyzero, args=(10, 0, excq))
    t.start()
    t.join()
    if excq.qsize() != 0:
        print(excq.get())
    
  2. No, it is not mandatory to use threading.Event.wait(). Your observation is right: calling event.wait() might make the thread hang indefinitely.

  3. goto 1;

\$\endgroup\$
  • \$\begingroup\$ You answered all my questions and confirmed by suspensions. Check mark coming your way. \$\endgroup\$ – user168710 May 2 '18 at 12:56