 Dando continuidade o artigo sobre thread, um recurso muito útil é lock reentrante.
 Dando continuidade o artigo sobre thread, um recurso muito útil é lock reentrante.Lock reentrante é uma variação de lock que pode ser realocado múltiplas vezes pelo mesmo thread e não pode ser liberado por outro thread. É muito útil em funções recursivas, mas funciona também para garantir que o lock seja alocado e liberado pelo mesmo thread.
A fábrica (factory) para criar locks reentrantes é
threading.RLock.É preciso tomar cuidado para que todos os threads compartilhem o mesmo lock, senão ele se torna inútil:
lock = RLock()
thr1 = Thread(target=func1, args=(lock, ))
thr2 = Thread(target=func2, args=(lock, ))
thr3 = Thread(target=func3, args=(lock, ))
Dentro da função, é preciso alocá-lo (
acquire) no inicío e liberá-lo (release) ao final. Por exemplo:
def func1(lock):
    lock.acquire()
    try:
        # executa o procedimento
        ...
    finally:
        lock.release()Protegendo um objeto mutável
Uma utilidade para o lock reentrante é proteger métodos que alterem o conteúdo de um objeto.
Imagine que temos uma classe
Podemos criar um decorador que torna um método thread-safe usando lock reentrante:
Imagine que temos uma classe
Person com dados, como identity_code (CPF) que podem sofrer alterações em threads diferentes (sei que não é uma boa abordagem, mas apenas como exemplo).Podemos criar um decorador que torna um método thread-safe usando lock reentrante:
def lock(wrapped):
    lock_ = RLock()
    @wraps(wrapped)
    def wrapper(*args, **kwargs):
        with lock_:
            return wrapped(*args, **kwargs)
    return wrapper
Esse decorador pode ser usado nos setters de cada propriedade:
class Person(object):
    ...
    @property
    def identity_code(self):
        return self.__identity_code
    @identity_code.setter
    @lock
    def identity_code(self, value):
        self.__identity_code = value
    ...
Na verdade essa abordagem não resolve 100% o problema, mas já reduz muito a ocorrência de bugs.
Protegendo qualquer objeto
Porém a abordagem acima apenas protege parcialmente o objeto e não funciona para classes de terceiros.
Outra abordagem é usar um lock para todo o objeto, tanto leitura quanto gravação, agnóstico a qual objeto está sendo protegido. Assim, não há necessidade de usarmos propriedades.
Vamos criar uma classe para trancar qualquer objeto:
Outra abordagem é usar um lock para todo o objeto, tanto leitura quanto gravação, agnóstico a qual objeto está sendo protegido. Assim, não há necessidade de usarmos propriedades.
Vamos criar uma classe para trancar qualquer objeto:
class ObjectLocker(object):
    def __init__(self, obj):
        self.__obj = obj
        self.__lock = RLock()
    def __enter__(self):
        self.__lock.acquire()
        return self.__obj
    def __exit__(self, etype, exc, traceback):
        self.__lock.release()
No código a instância dessa classe será passada para os threads, que terá de usar 
Ao usar
A passagem será:
with para acessar o objeto original.Ao usar
with, o objeto original será trancado para o thread atual e liberado ao final.A passagem será:
locker = ObjectLocker(Person(...))
thr = Thread(target=func, args=(locker, ))
Dentro da(s) função(ões) 
func a instância de Person deve ser acessa da seguinta forma:
with locker as person:
    name = person.name
    person.identity_code = data['identity_code']
Espero que os exemplos tenham sido úteis.
[]’sCacilhας, La Batalema
 
 
 CC-BY: Os textos deste blog podem ser reporduzidos contanto que sejam informados autor e origem.
 CC-BY: Os textos deste blog podem ser reporduzidos contanto que sejam informados autor e origem.