from contextlib import contextmanager
from threading import Thread, Lock, currentThread
LOCK = None
@contextmanager
def _with_Lock( ) :
"" "
I have emulated a thread losing control at an inconvenient time
by using calls to time.sleep(). You will observe that each thread
acquires a different Lock instance. Thus, there is no serialization.
Moreover, task B is issuing a call to release against the Lock instance
that task A acquired. Calling release twice on a Lock that
has not been acquired will cause thread B to raise an exception.
" ""
name = currentThread( ) .name
# This is a singleton, pylint: disable=global-statement
global LOCK
if LOCK is None:
if name == 'A' :
time .
sleep ( 1 ) # emulate losing control LOCK = Lock( )
print( name, id( LOCK) , 'created' )
LOCK.acquire ( )
print( name, id( LOCK) , 'acquired' )
yield
if name == 'B' :
print( name, id( LOCK) , 'released' )
LOCK.release ( )
def foo( ) :
with _with_Lock( ) :
pass
t1 = Thread( target= foo, name= 'A' )
t2 = Thread( target= foo, name= 'B' )
t1.start ( )
# Give t1 a chance to run and then sleep before we start t2:
t2.start ( )
t1.join ( )
t2.join ( )
ZnJvbSBjb250ZXh0bGliIGltcG9ydCBjb250ZXh0bWFuYWdlcgpmcm9tIHRocmVhZGluZyBpbXBvcnQgVGhyZWFkLCBMb2NrLCBjdXJyZW50VGhyZWFkCmltcG9ydCB0aW1lCgpMT0NLID0gTm9uZQoKQGNvbnRleHRtYW5hZ2VyCmRlZiBfd2l0aF9Mb2NrKCk6CiAgICAiIiIKICAgIEkgaGF2ZSBlbXVsYXRlZCBhIHRocmVhZCBsb3NpbmcgY29udHJvbCBhdCBhbiBpbmNvbnZlbmllbnQgdGltZQogICAgYnkgdXNpbmcgY2FsbHMgdG8gdGltZS5zbGVlcCgpLiBZb3Ugd2lsbCBvYnNlcnZlIHRoYXQgZWFjaCB0aHJlYWQKICAgIGFjcXVpcmVzIGEgZGlmZmVyZW50IExvY2sgaW5zdGFuY2UuIFRodXMsIHRoZXJlIGlzIG5vIHNlcmlhbGl6YXRpb24uCiAgICBNb3Jlb3ZlciwgdGFzayBCIGlzIGlzc3VpbmcgYSBjYWxsIHRvIHJlbGVhc2UgYWdhaW5zdCB0aGUgTG9jayBpbnN0YW5jZQogICAgdGhhdCB0YXNrIEEgYWNxdWlyZWQuIENhbGxpbmcgcmVsZWFzZSB0d2ljZSBvbiBhIExvY2sgdGhhdAogICAgaGFzIG5vdCBiZWVuIGFjcXVpcmVkIHdpbGwgY2F1c2UgdGhyZWFkIEIgdG8gcmFpc2UgYW4gZXhjZXB0aW9uLgogICAgIiIiCgogICAgbmFtZSA9IGN1cnJlbnRUaHJlYWQoKS5uYW1lCiAgICAjIFRoaXMgaXMgYSBzaW5nbGV0b24sIHB5bGludDogZGlzYWJsZT1nbG9iYWwtc3RhdGVtZW50CiAgICBnbG9iYWwgTE9DSwoKICAgIGlmIExPQ0sgaXMgTm9uZToKICAgICAgICBpZiBuYW1lID09ICdBJzoKICAgICAgICAgICAgdGltZS5zbGVlcCgxKSAjIGVtdWxhdGUgbG9zaW5nIGNvbnRyb2wKICAgICAgICBMT0NLID0gTG9jaygpCiAgICAgICAgcHJpbnQobmFtZSwgaWQoTE9DSyksICdjcmVhdGVkJykKCiAgICBMT0NLLmFjcXVpcmUoKQogICAgcHJpbnQobmFtZSwgaWQoTE9DSyksICdhY3F1aXJlZCcpCiAgICB5aWVsZAogICAgaWYgbmFtZSA9PSAnQic6CiAgICAgICAgdGltZS5zbGVlcCgxKQogICAgcHJpbnQobmFtZSwgaWQoTE9DSyksICdyZWxlYXNlZCcpCiAgICBMT0NLLnJlbGVhc2UoKQoKZGVmIGZvbygpOgogICAgd2l0aCBfd2l0aF9Mb2NrKCk6CiAgICAgICAgcGFzcwoKdDEgPSBUaHJlYWQodGFyZ2V0PWZvbywgbmFtZT0nQScpCnQyID0gVGhyZWFkKHRhcmdldD1mb28sIG5hbWU9J0InKQp0MS5zdGFydCgpCiMgR2l2ZSB0MSBhIGNoYW5jZSB0byBydW4gYW5kIHRoZW4gc2xlZXAgYmVmb3JlIHdlIHN0YXJ0IHQyOgp0aW1lLnNsZWVwKC41KQp0Mi5zdGFydCgpCnQxLmpvaW4oKQp0Mi5qb2luKCk=
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