Make a small function thread safe

Ian Kelly ian.g.kelly at gmail.com
Mon Dec 19 01:57:43 EST 2011


On Sun, Dec 18, 2011 at 6:27 PM, Tim Delaney
<timothy.c.delaney at gmail.com> wrote:
> On 18 December 2011 19:52, RangerElf <gustavo.cordova at gmail.com> wrote:
>>
>> Which is why the original .acquire() ... .release() idiom was wrong, this
>> would better express the intent:
>>
>> try:
>>  lock.acquire()
>>  shared_container.append(...)
>> finally:
>>  lock.release()
>
>
> No - this is very bad. The lock must be acquired outside the try: -
> otherwise if an exception is thrown while acquiring, you will try to release
> a lock that you have not acquired.
>
> Which again is why using with is a much better option - you can't make this
> kind of mistake.

Well, not unless you make the same mistake in the context manager itself.

from contextlib import contextmanager

@contextmanager
def bad_context_manager(lock):
    try:
        lock.acquire()
        yield
    finally:
        lock.release()

class Lock(object):
    def __init__(self):
        self.is_locked = False
    def acquire(self):
        assert False
    def release(self):
        assert self.is_locked, "Tried to release lock without acquiring it"

with bad_context_manager(Lock()):
    pass

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.7/contextlib.py", line 17, in __enter__
    return self.gen.next()
  File "<stdin>", line 7, in bad_context_manager
  File "<stdin>", line 7, in release
AssertionError: Tried to release lock without acquiring it


Perhaps a (small) reason to avoid the contextmanager decorator and
implement __enter__ and __exit__ instead.



More information about the Python-list mailing list