48

I've seen a lot of Python scripts that use Threads in a class and a lot of them use the threading.Event(). For example:

class TimerClass(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.event = threading.Event()

    def run(self):
        while not self.event.is_set():
            print("something")
            self.event.wait(120)

In the while loop, why do they check the condition if they don't set self.event?

Guillaume Jacquenot
  • 11,217
  • 6
  • 43
  • 49
user2724899
  • 483
  • 1
  • 4
  • 4
  • 1
    More correct probably: `while not self.event.wait(120): print "something"` – Mr_and_Mrs_D Sep 17 '18 at 21:43
  • 1
    @Mr_and_Mrs_D If this is (more) correct can't be determined unless you know it is really okay to wait _before_ the `print`. If the Thread should do the first `print` or whatever work immediately after starting the thread then it is incorrect to wait in the ``while`` condition expression. – BlackJack Jun 27 '19 at 12:33

1 Answers1

62

Because someone else will set it.

You generally start a thread in one part of your application and continue to do whatever you do:

thread = TimerClass()
thread.start()
# Do your stuff

The thread does it's stuff, while you do your stuff. If you want to terminate the thread you just call:

thread.event.set()

And the thread will stop.

So the answer is: event, in this case, is not used for controlling the thread from inside the thread object itself. It is used for controlling the thread from outside (from the object which holds the reference to the thread).

tshepang
  • 12,111
  • 21
  • 91
  • 136
Viktor Kerkez
  • 45,070
  • 12
  • 104
  • 85
  • In OP's example, does the self.event.wait(120) mean that it will wait 120 seconds to redo the loop, unless the event gets set() before those 120 seconds is up, at which point self.event.wait(120) will be return, and basically the loop will exit without printing again? And if the event never gets set(), it'll just print "something" everything 120 seconds? – gunit Jan 04 '18 at 09:12
  • 3
    @gunit It will print "something" every 120s until the event is set. When the event is set the loop will exit. – Viktor Kerkez Jan 04 '18 at 17:59
  • I have a question. What's the advantage of an Event over a simple boolean instance variable? Let's say I set `self.stop = False` in `__init__` and execute `while not self.stop: ...` in `run`. – timgeb Feb 22 '21 at 19:24
  • @timgeb Event will notify all the listeners when it's set so you don't have to run the while loop for every listener that's checking the event state. – Viktor Kerkez Feb 23 '21 at 20:06
  • Sorry I didn't get that at all. :) What do you mean, "run the while loop for every listener"? What while loop (I suppose you don't mean the while True loop in the question)? What listener? – timgeb Feb 24 '21 at 08:23
  • 1
    @timgeb Ah sorry I just skimmed your example. I thought you're waiting for the event: `while not self.stop: time.wait()`. If that was the case you don't need loop, you can just do `self.event.wait()` and that will return when the the event is set. The problem in your example is that setting an instance variable is not threadsafe in python, and events are. So if you're changing an instance variable from multiple threads you can end up with an undefined state. If there is only one mutator thread then yes there is no difference but I would still use an event since it's future proof. – Viktor Kerkez Feb 25 '21 at 11:44
  • 1
    @timgeb I believe accessing and setting a variable (a named-reference) pointing to a boolean is "atomic" and therefore thread-safe. However self.stop has a period in the named-reference and therefore the "period" causes a dereference event within Python. The de-reference event is not thread-safe I believe. But in this case it would not matter as the conditional is checked after the de-reference anyway. Using a global, often considered a no-no, would avoid the de-reference event, so long as you are accessing it only within the same module. – DevPlayer Jun 01 '21 at 21:30