Search code examples
pythoneventsros

Why is the 'else:' part of my while loop not being called?


I am trying to control two threads in my python code with event objects. In one thread I wait for the other thread to set the event to True and execute while it stays as 'True' and else, do something else. My problem is that the 'else' part of the loop never gets called. Any ideas why? Thank you very much in advance.

I have tried changing the 'while'statement for a 'if' statement without any luck. And I really don't know why this isn't working.

def send_thread(self):
        rospy.loginfo('set')
        self.event.set()
        for cmd in sequence:
            Do something
        rospy.sleep(2)
        rospy.loginfo('saving command in full')
        self.U_full.append(self.U_single)
        self.event.clear()
def receive_thread(self,msg):
        while self.event.wait() == True:
           Do something
        else:
           Do something else

The expected result is that the 'while' part of the recive_thread runs until the event is cleared in the sending_thread and after that, the 'else' part is executed.


Solution

  • You are waiting on an event without a timeout, so self.event.wait() is always going to return True. From the threading.Event.wait() documentation:

    This method returns true if and only if the internal flag has been set to true, either before the wait call or after the wait starts, so it will always return True except if a timeout is given and the operation times out.

    Bold emphasis mine. Because it always returns true, you'll never see the else: suite executed.

    Just so this is clear: the while loop will never exit if you don't use a timeout, because without a timeout, event.wait() will only return when the event flag is true. Clearing the flag with self.event.clear() sets the flag to False and so event.wait() simply won't return.

    Use a timeout:

    while self.event.wait(0.1):
        # do something while the event is still set
    else:
        # the event has been cleared and the timeout has been reached!
        # This block can be skipped if the while block uses 'break'
    

    Note: it is enough to test self.event.wait(). The == True part is entirely redundant. Also, a while test: ... else: ... setup only makes sense if you are going to use break in the while loop, to explicitly skip the else block. If you are not using break in the while block, you may as well remove the else: and just run the code indented in that block unconditionally.

    Alternatively, test for event.is_set():

    while event.is_set():
        # do something while the event is still set
    else:
        # the event has been cleared and 'break' was not used
    

    Or, invert the event, clear it at the start, and set the event only when done:

    def send_thread(self):
        rospy.loginfo('set')
        self.event.clear()
        for cmd in sequence:
            Do something
        rospy.sleep(2)
        rospy.loginfo('saving command in full')
        self.U_full.append(self.U_single)
        self.event.set()
    

    then wait for the event to be set:

    if self.event.wait():
        # blocks until the event has been set by the other thread.
    

    or if you want to do things before then, use a not self.event.wait(0.1) or simply not self.event.is_set() in your while loop.