from contextlib import contextmanager
from threading import Thread, Lock, currentThread
import time

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':
        time.sleep(1)
    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:
time.sleep(.5)
t2.start()
t1.join()
t2.join()