2015-07-19 2 views
2

Насколько я знаю, следующий код будет заблокирован, если lock уже получен другим потоком.блокировка блокировки с оператором 'with'

Кажется, что неблокирование может быть реализовано lock.acquire(0), но вместо этого я должен использовать блок try-finally вместо with.

lock = threading.Lock() 

def func(): 
with lock: 
    # do something... 

Есть ли способ реализации неблокируемой приобретения замка?

ответ

0

Весь смысл блокировки состоит в том, чтобы определенные разделы вашей программы выполнялись только по одному потоку или процессу за раз. Это достигается путем блокировки любых потоков/процессов, пытающихся получить блокировку, в то время как что-то еще удерживает ее.

Если вы не хотите, чтобы блокировка блокировалась, почему вы используете блокировку в первую очередь? Предположительно, чтобы вы могли сделать что-то еще, пока вы ждете?

Чтобы попытаться приобрести замок l без блокировки, позвоните по телефону l.acquire(blocking=False). Это немедленно вернет False, если замок не был приобретен. Если замок был, он возвращает True, и вы продолжаете удерживать замок до тех пор, пока не назовете его метод release().

Эта форма, однако, не особенно полезна в операторе with. Обычно вам нужен управляемый код (с отступом после with) для запуска только после того, как блокировка была приобретена. не запрашивать, имеет ли он или нет, и предпринять два альтернативных действия.

+0

Спасибо. Я должен использовать 'lock.acquire()'. : D. –

7

Есть ли способ реализовать блокировку блокировки блокировки?

Да. Просто поднимите исключение, если замок не может быть получен немедленно. Что-то вроде:

@contextlib.contextmanager 
def non_blocking_lock(lock=threading.Lock()): 
    if not lock.acquire(blocking=False): 
     raise WouldBlockError 
    try: 
     yield lock 
    finally: 
     lock.release() 

Использование:

with non_blocking_lock(): 
    # run with the lock acquired 
+0

Спасибо за ответ. В любом случае я должен использовать функцию 'lock.acquire'. –

+0

Я не понимаю смысл аргумента по умолчанию. Блокировка будет создана внутри диспетчера контекстов и будет предоставлена ​​обратно пользователю, который уже заблокирован, но, конечно, ни один другой поток не знает об этой блокировке. В каком сценарии это может быть полезно? – max

+0

@max: Это не так, как аргументы по умолчанию работают в Python. Понимание [«Наименьшее изумление» и параметр Mutable Default Argument] (http://stackoverflow.com/q/1132941/4279) может помочь. – jfs

2

Вы можете реализовать свой собственный замок, как объект, который ведет себя, как вы хотите:

class MyLock(object): 
    def __init__(self): 
     self._lock = threading.Lock() 
     self._locked = False 
    def __enter__(self): 
     locked = self._lock.acquire(False) 
     self._locked = locked 
     return locked 
    def __exit__(self, *args): 
     if self._locked: 
      self._lock.release() 
      self._locked = False 

Я тестировал его с этим кодом:

In [5]: ml = MyLock() 
In [6]: with ml as aq: 
    ...:  print "got it?!", aq 
    ...:  with ml as try2: 
    ...:   print "got it second time???", try2 

Выход:

got it?! True 
got it second time??? False 

Вы также можете использовать методы блокировки и разблокировки, которые используют внутренний замок, но вы получаете изображение, соответствующее его потребностям.

+0

Вы должны быть осторожны с этим шаблоном, так как этот внутренний замок фактически освободит ваш замок! Код в другом "с m1 как try3: 'на том же уровне, что и внутренний замок. –

4
@contextmanager 
def nonblocking(lock): 
    locked = lock.acquire(False) 
    try: 
     yield locked 
    finally: 
     if locked: 
      lock.release() 

lock = threading.Lock() 
with nonblocking(lock) as locked: 
    if locked: 
     do_stuff() 
+0

Добро пожаловать в переполнение стека! Хотя этот код может ответить на вопрос, предоставляя дополнительный контекст относительно того, почему и/или как этот код отвечает на вопрос, улучшает его долгосрочную ценность. Кодовые ответы не приветствуются. – Ajean

Смежные вопросы