2

I am building a GUI application for serial communication with a digital pump. I got stuck in the update mechanism used for fetching information from it. The update_values method is called every 5 seconds using a QTimer (From the PySide module), but the user can specifically order an update by calling the same method. For that reason I only want just one thread to run on the update_values code. However this doesn't seem to work using either a semaphore or a Lock as more than one thread enter the semaphore block at will:

self.update_sema = threading.Semaphore(value=1)

... ...

def update_values(self, initialize = False):
    """This is the parameters update method."""

    self.update_sema.acquire(False)
    print "ENTERED THE SEMAPHORE"
    self.update_thread = threading.Thread(\
            target = self.actual_update_method,\
            args = (initialize,))
    self.update_thread.start()
def actual_update_method(self, initialize):

    # reading info mechanism
    self.status["absolute_pos"] = self.send_Command('?', 10)[3:]
    self.status["actual_pos"] = self.send_Command('?4', 10)[3:]
    self.status["starting_vel"] = self.send_Command('?1', 10)[3:]
    self.status["top_vel"] = self.send_Command('?2', 10)[3:]
    self.status["cutoff_vel"] = self.send_Command('?3', 10)[3:]
    self.status["backlash_steps"] = self.send_Command('?12', 10)[3:]
    self.status["fluid_sensor"] = self.send_Command('?22', 10)[3:]
    self.status["buffer_status"] = self.send_Command('?F', 10)[3:]

    # These must be asked only once, at the initialization phase
    if initialize:
        #print "version set as well!"
        self.status["version"] = self.send_Command('?&', 10)[3:]
        self.status["checksum"] = self.send_Command('?#', 10)[3:]

    self.update_sema.release()
    print "EXITED THE SEMAPHORE"

1 Answer 1

3

Because you're using a non-blocking call to acquire (by using acquire(blocking=False)), you need to make sure you only continue on in the method if you actually acquired the semaphore, like this:

def update_values(self, initialize = False):
    """This is the parameters update method."""

    if self.update_sema.acquire(False):
        print "ENTERED THE SEMAPHORE"
        self.update_thread = threading.Thread(\
                target = self.actual_update_method,\
                args = (initialize,))
        self.update_thread.start()

This behavior is described in the documentation:

acquire([blocking])

When invoked without arguments: if the internal counter is larger than zero on entry, decrement it by one and return immediately. If it is zero on entry, block, waiting until some other thread has called release() to make it larger than zero. This is done with proper interlocking so that if multiple acquire() calls are blocked, release() will wake exactly one of them up. The implementation may pick one at random, so the order in which blocked threads are awakened should not be relied on. There is no return value in this case.

When invoked with blocking set to true, do the same thing as when called without arguments, and return true.

When invoked with blocking set to false, do not block. If a call without an argument would block, return false immediately; otherwise, do the same thing as when called without arguments, and return true.

Sign up to request clarification or add additional context in comments.

2 Comments

Wow! Something so simple and yet so troublesome... But I still don't get it. As you stated above "When invoked with blocking set to false, do not block". Doesn't this mean that if a thread finds the semaphore locked, then it should run the next line AFTER the critical section, after the update_sema.release()?
@buttercookie Nope, it just means that acquire will return False and continue on to the next line in the code. There's no reliable way for the code to just skip ahead to where you release the semaphore. Take your example, the semaphore gets released in a completely different thread. The program can't skip ahead to that line, because if it can't acquire the semaphore, it's not allowed to create the thread to begin with. It's up to you have the correct logic to send the program to whatever you deem the "right place" if the acquire call returns False.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.