fork download
  1. from contextlib import contextmanager
  2. from threading import Thread, Lock, currentThread
  3. import time
  4.  
  5. LOCK = None
  6.  
  7. @contextmanager
  8. def _with_Lock():
  9. """
  10. I have emulated a thread losing control at an inconvenient time
  11. by using calls to time.sleep(). You will observe that each thread
  12. acquires a different Lock instance. Thus, there is no serialization.
  13. Moreover, task B is issuing a call to release against the Lock instance
  14. that task A acquired. Calling release twice on a Lock that
  15. has not been acquired will cause thread B to raise an exception.
  16. """
  17.  
  18. name = currentThread().name
  19. # This is a singleton, pylint: disable=global-statement
  20. global LOCK
  21.  
  22. if LOCK is None:
  23. if name == 'A':
  24. time.sleep(1) # emulate losing control
  25. LOCK = Lock()
  26. print(name, id(LOCK), 'created')
  27.  
  28. LOCK.acquire()
  29. print(name, id(LOCK), 'acquired')
  30. yield
  31. if name == 'B':
  32. time.sleep(1)
  33. print(name, id(LOCK), 'released')
  34. LOCK.release()
  35.  
  36. def foo():
  37. with _with_Lock():
  38. pass
  39.  
  40. t1 = Thread(target=foo, name='A')
  41. t2 = Thread(target=foo, name='B')
  42. t1.start()
  43. # Give t1 a chance to run and then sleep before we start t2:
  44. time.sleep(.5)
  45. t2.start()
  46. t1.join()
  47. t2.join()
Success #stdin #stdout #stderr 0.03s 10612KB
stdin
Standard input is empty
stdout
B 23428814822416 created
B 23428814822416 acquired
A 23428814822336 created
A 23428814822336 acquired
A 23428814822336 released
B 23428814822336 released
stderr
Exception in thread B:
Traceback (most recent call last):
  File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.7/threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "./prog.py", line 38, in foo
    pass
  File "/usr/lib/python3.7/contextlib.py", line 119, in __exit__
    next(self.gen)
  File "./prog.py", line 34, in _with_Lock
    LOCK.release()
RuntimeError: release unlocked lock