web-dev-qa-db-fra.com

Python Conception de verrouillage conditionnelle "avec"

J'essaie de faire un verrouillage partagé avec des instructions

def someMethod(self, hasLock = False):
     with self.my_lock:
         self.somethingElse(hasLock=True)


def somethingElse(self, hasLock = False):
    #I want this to be conditional...
    with self.my_lock:
          print 'i hate hello worlds"

C'est logique? Je veux simplement faire avec si je n'ai pas encore le verrou.

En plus de pouvoir accomplir cela, est-ce une mauvaise conception? Dois-je simplement m'acquérir/me libérer?

20
Nix

Utilisez simplement un threading.RLock qui est rentrant, ce qui signifie qu'il peut être acquis plusieurs fois par le même thread.

http://docs.python.org/library/threading.html#rlock-objects

Pour plus de clarté, le RLock est utilisé dans les instructions with, comme dans votre exemple de code:

lock = threading.RLock()

def func1():
    with lock:
        func2()

def func2():
    with lock: # this does not block even though the lock is acquired already
        print 'hello world'

En ce qui concerne la mauvaise conception ou non, nous aurions besoin de plus de contexte. Pourquoi les deux fonctions doivent-elles acquérir le verrou? Quand est func2 appelé par autre chose que func1?

39
FogleBird

Le Python or est court-circuitage afin que vous puissiez rendre le verrouillage conditionnel:

def somethingElse(self, hasLock = False):
    #I want this to be conditional...
    with hasLock or self.my_lock:
          print 'i hate hello worlds'

Malheureusement, ce n'est pas si simple, car un booléen n'est pas un retour valide à partir d'une instruction with. Vous devrez créer une classe avec le __enter__ et __exit__ pour encapsuler la valeur booléenne True.

Voici une implémentation possible que je n'ai pas testée.

from contextlib import contextmanager

@contextmanager
def withTrue():
    yield True

def withbool(condition):
    if condition:
        return withTrue()
    return False

def somethingElse(self, hasLock = False):
    with withbool(hasLock) or self.my_lock():
          print 'i hate hello worlds'

C'est beaucoup de passe-partout pour quelque chose de si simple, donc la solution RLock ressemble à un gagnant. Cette solution pourrait cependant être utile dans un contexte différent.

5
Mark Ransom

Pourquoi pas:

def someMethod(self):
     with self.my_lock:
         self.somethingNoLock()

def somethingElse(self):
    with self.my_lock:
         self.somethingNoLock()

def somethingNoLock(self):
    print 'i hate hello worlds"

Notez que si someMethod et somethingElse sont identiques dans ma solution, en général ils seraient différents. Vous pouvez placer un autre wrapper autour de somethingNoLock afin que l'acquisition et la libération du verrou ne soient pas répétées plusieurs fois.

C'est beaucoup plus simple et direct. Tout simplement parce que le marteau de verrouillage rentrant est disponible, je ne recommanderais pas de l'utiliser lorsqu'il existe un moyen plus simple et moins fragile de le clouer.

La critique plus spécifique de rlock est que la ligne qui crée le verrou rentrant est loin du code qui acquiert le verrou de manière rentrante. Ceci est légèrement fragile si quelqu'un dit fusionne le verrou rentrant avec un autre verrou qui n'est pas rentrant ou change autrement la ligne qui crée le verrou.

1
Real Geek N

Il est préférable d'utiliser l'instruction with que les fonctions acquire() et release(). De cette façon, si une erreur se produit, les verrous seront libérés.

0
Utku Zihnioglu

L'instruction with est un excellent moyen d'implémenter le verrouillage, car le verrouillage est un modèle d'acquisition de ressources parfait. Cependant, votre exemple actuel ne fonctionnera pas, vous aurez besoin d'une instruction if autour de l'instruction with dans quelque chose d'Else ().

0
guidoism