How to implement a Lock with a timeout in Python 2.7

前端 未结 7 808
执笔经年
执笔经年 2020-12-03 07:09

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

相关标签:
7条回答
  • 2020-12-03 07:57

    Okay, this is already implemented in python 3.2 or above: https://docs.python.org/3/library/threading.html Look for threading.TIMEOUT_MAX

    But I improved on the test case over frans' version ... though this is already a waste of time if you're on py3.2 or above:

    from unittest.mock import patch, Mock
    import unittest
    
    import os
    import sys
    import logging
    import traceback
    import threading
    import time
    
    from Util import ThreadingUtil
    
    class ThreadingUtilTests(unittest.TestCase):
    
        def setUp(self):
            pass
    
        def tearDown(self):
            pass
    
        # https://www.pythoncentral.io/pythons-time-sleep-pause-wait-sleep-stop-your-code/
        def testTimeoutLock(self):
    
            faulted = [False, False, False]
    
            def locking_thread_fn(threadId, lock, duration, timeout):
                try:
                    threadName = "Thread#" + str(threadId)
                    with ThreadingUtil.TimeoutLock(threadName, lock, timeout=timeout, raise_on_timeout=True):
                        print('%x: "%s" begins to work..' % (threading.get_ident(), threadName))
                        time.sleep(duration)
                        print('%x: "%s" finished' % (threading.get_ident(), threadName))
                except:
                    faulted[threadId] = True
    
            _lock = ThreadingUtil.TimeoutLock.lock()
    
            _sleepDuration = [5, 10, 1]
            _threads = []
    
            for i in range(3):
                _duration = _sleepDuration[i]
                _timeout = 6
                print("Wait duration (sec): " + str(_duration) + ", Timeout (sec): " + str(_timeout))
                _worker = threading.Thread(
                                            target=locking_thread_fn, 
                                            args=(i, _lock, _duration, _timeout)
                                        )
                _threads.append(_worker)
    
            for t in _threads: t.start()
            for t in _threads: t.join()
    
            self.assertEqual(faulted[0], False)
            self.assertEqual(faulted[1], False)
            self.assertEqual(faulted[2], True)
    

    Now under "Util" folder, I have "ThreadingUtil.py":

    import time
    import threading
    
    # https://stackoverflow.com/questions/8392640/how-to-implement-a-lock-with-a-timeout-in-python-2-7
    # https://docs.python.org/3.4/library/asyncio-sync.html#asyncio.Condition
    # https://stackoverflow.com/questions/28664720/how-to-create-global-lock-semaphore-with-multiprocessing-pool-in-python
    # https://hackernoon.com/synchronization-primitives-in-python-564f89fee732
    
    class TimeoutLock(object):
        ''' taken from https://stackoverflow.com/a/8393033/1668622
        '''
        class lock:
            def __init__(self):
                self.owner = None
                self.lock = threading.Lock()
                self.cond = threading.Condition()
    
            def _release(self):
                self.owner = None
                self.lock.release()
                with self.cond:
                    self.cond.notify()
    
        def __init__(self, owner, lock, timeout=1, raise_on_timeout=False):
            self._owner = owner
            self._lock = lock
            self._timeout = timeout
            self._raise_on_timeout = raise_on_timeout
    
        # http://effbot.org/zone/python-with-statement.htm
        def __enter__(self):
            self.acquire()
            return self
    
        def __exit__(self, type, value, tb):
            ''' will only be called if __enter__ did not raise '''
            self.release()
    
        def acquire(self):
            if self._raise_on_timeout:
                if not self._waitLock():
                    raise RuntimeError('"%s" could not aquire lock within %d sec'
                                       % (self._owner, self._timeout))
            else:
                while True:
                    if self._waitLock():
                        break
                    print('"%s" is waiting for "%s" and is getting bored...'
                          % (self._owner, self._lock.owner))
            self._lock.owner = self._owner
    
        def release(self):
            self._lock._release()
    
        def _waitLock(self):
            with self._lock.cond:
                _current_t = _start_t = time.time()
                while _current_t < _start_t + self._timeout:
                    if self._lock.lock.acquire(False):
                        return True
                    else:
                        self._lock.cond.wait(self._timeout - _current_t + _start_t)
                        _current_t = time.time()
            return False
    
    0 讨论(0)
提交回复
热议问题