Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a Lock with a timeout in Python 2.7

Tags:

Is there a way to implement a lock in Python for multithreading purposes whose acquire method can have an arbitrary timeout? The only working solutions I found so far use polling, which

  • I find inelegant and inefficient
  • Doesn't preserve the bounded waiting / progress guarantee of the lock as a solution to the critical section problem

Is there a better way to implement this?

like image 305
Niklas B. Avatar asked Dec 05 '11 22:12

Niklas B.


People also ask

What does lock () do in Python?

Using Lock to prevent the race condition To prevent race conditions, you can use the Lock class from the threading module. A lock has two states: locked and unlocked. By default, the lock has the unlocked status until you acquire it.

How do you lock in Python?

A lock can be locked using the acquire() method. Once a thread has acquired the lock, all subsequent attempts to acquire the lock are blocked until it is released. The lock can be released using the release() method. Calling the release() method on a lock, in an unlocked state, results in an error.

How do you Timeout a thread in Python?

Applying timeout function using thread in PythonThe first thread is to execute the function. 2. The second thread is to measure the time taken by the function. The only second thread should whether the time is over or not.

Can you lock a variable in Python?

You can lock a variable to make it thread-safe using a threading. Lock. In this tutorial you will discover how to lock a variable in Python.


2 Answers

to elaborate on Steven's comment suggestion:

import threading import time  lock = threading.Lock() cond = threading.Condition(threading.Lock())  def waitLock(timeout):     with cond:         current_time = start_time = time.time()         while current_time < start_time + timeout:             if lock.acquire(False):                 return True             else:                 cond.wait(timeout - current_time + start_time)                 current_time = time.time()     return False 

Things to notice:

  • there are two threading.Lock() objects, one is internal to the threading.Condition().
  • when manipulating cond, it's lock is acquired; the wait() operation unlocks it, though, so any number of threads can watch it.
  • the wait is embedded inside a for loop that keeps track of the time. threading.Condition can become notified for reasons other than timeouts, so you still need to track the time if you really want it to expire.
  • even with the condition, you still 'poll' the real lock, because its possible for more than one thread to wake and race for the lock. if the lock.acquire fails, the loop returns to waiting.
  • callers of this waitLock function should follow a lock.release() with a cond.notify() so that other threads waiting on it are notified that they should retry aquiring the lock. This is not shown in the example.
like image 87
SingleNegationElimination Avatar answered Oct 18 '22 15:10

SingleNegationElimination


My version using thread safe queues http://docs.python.org/2/library/queue.html and their put/get methods that supports timeout.

Until now is working fine, but if someone can do a peer review on it I'll be grateful.

""" Thread-safe lock mechanism with timeout support module. """  from threading import ThreadError, current_thread from Queue import Queue, Full, Empty   class TimeoutLock(object):     """     Thread-safe lock mechanism with timeout support.     """      def __init__(self, mutex=True):         """         Constructor.         Mutex parameter specifies if the lock should behave like a Mutex, and         thus use the concept of thread ownership.         """         self._queue = Queue(maxsize=1)         self._owner = None         self._mutex = mutex      def acquire(self, timeout=0):         """         Acquire the lock.         Returns True if the lock was succesfully acquired, False otherwise.          Timeout:         - < 0 : Wait forever.         -   0 : No wait.         - > 0 : Wait x seconds.         """         th = current_thread()         try:             self._queue.put(                 th, block=(timeout != 0),                 timeout=(None if timeout < 0 else timeout)             )         except Full:             return False          self._owner = th         return True      def release(self):         """         Release the lock.         If the lock is configured as a Mutex, only the owner thread can release         the lock. If another thread attempts to release the lock a         ThreadException is raised.         """         th = current_thread()         if self._mutex and th != self._owner:             raise ThreadError('This lock isn\'t owned by this thread.')          self._owner = None         try:             self._queue.get(False)             return True         except Empty:             raise ThreadError('This lock was released already.') 
like image 31
Havok Avatar answered Oct 18 '22 17:10

Havok