Sono nuovo in Python e sto attualmente cercando di imparare il threading. Sono stanco di usare i lock per rendere le mie risorse thread-safe perché non sono intrinsecamente legate alla risorsa, quindi sono costretto a dimenticarmi di acquisirle e/o rilasciarle ogni volta che il mio codice interagisce con la risorsa. Invece, mi piacerebbe essere in grado di "avvolgere" (o decorare?) Un oggetto in modo che tutti i suoi metodi e getter/setter di attributi siano atomici. qualcosa di simile:Come decorare un oggetto Python con un mutex
È possibile? In tal caso, qual è il modo di "miglior prassi" per implementarlo?
MODIFICA: una buona risposta alla domanda precedente è disponibile in How to make built-in containers (sets, dicts, lists) thread safe?. Però; come hanno dimostrato sia abarnert che jsbueno, la soluzione che ho proposto (automatizzare i lock) non è generalmente una buona idea, perché determinare la granularità corretta delle operazioni atomiche richiede un po 'di intelligenza ed è probabilmente difficile (o impossibile) automatizzare correttamente.
Il problema rimane comunque che i blocchi non sono vincolati in alcun modo alle risorse che intendono proteggere, quindi la mia nuova domanda è: Qual è un buon modo per associare un lucchetto a un oggetto?
Soluzione proposta # 2: Immagino che ci potrebbe essere un modo per associare un blocco per un oggetto in modo tale che il tentativo di accedere a tale oggetto senza prima acquisire il blocco genera un errore, ma posso vedere come sia potuto ottenere ingannevole .
MODIFICA: il seguente codice non è molto pertinente alla domanda. L'ho pubblicato per dimostrare che avevo provato a risolvere il problema me stesso e mi sono perso prima di pubblicare questa domanda.
Per la cronaca, ho scritto il seguente codice, ma non funziona:
import threading
import types
import inspect
class atomicObject(object):
def __init__(self, obj):
self.lock = threading.RLock()
self.obj = obj
# keep track of function handles for lambda functions that will be created
self.funcs = []
# loop through all the attributes of the passed in object
# and create wrapped versions of each attribute
for name in dir(self.obj):
value = getattr(self.obj, name)
if inspect.ismethod(value):
# this is where things get really ugly as i try to work around the
# limitations of lambda functions and use eval()... I'm not proud of this code
eval("self.funcs.append(lambda self, *args, **kwargs: self.obj." + name + "(*args, **kwargs))")
fidx = str(len(self.funcs) - 1)
eval("self." + name + " = types.MethodType(lambda self, *args, **kwargs: self.atomize(" + fidx + ", *args, **kwargs), self)")
def atomize(self, fidx, *args, **kwargs):
with self.lock:
return self.functions[fidx](*args, **kwargs)
posso creare un atomicObject (dict()), ma quando provo ad aggiungere un valore all'oggetto , Ottengo l'errore; "atomicObject non supporta l'assegnazione degli articoli".
Inoltre, questo codice non si avvicina nemmeno alla corsa. Ti mancano due punti dopo l'istruzione 'with', hai il nome sbagliato per il tipo' Lock', e non ho idea di cos'altro potrebbe andare oltre. Come ti aspetti che eseguiamo il debugging per te se non siamo nemmeno in grado di iniziare? – abarnert
Inoltre, di solito non è un'idea così buona come sembra. Per esempio, se creo 'd = atomicObject (dict())', quindi 'd ['abc'] = 3', quindi' d ['abc'] + = 1', che non è atomico - atomicamente legge 'd ['abc']', quindi rilascia il blocco, quindi scrive atomicamente 'd ['abc']', sovrascrivendo qualsiasi altra scrittura fatta nell'intervallo di tempo. (Immagina che 'd' fosse un segnalino e che avessi 20 thread tutti cercando di fare +1 allo stesso tempo. Invece di salire +20, probabilmente salirà +1 o +2 circa.) – abarnert
I Mi dispiace per il codice sciatto. È il prodotto della tortura e della frustrazione. Penso di aver corretto alcuni degli errori, ma in genere ho incluso il mio codice come cortesia. Speravo che qualcuno potesse indicarmi la giusta direzione dato che sono ovviamente perso. La tua risposta è stata di enorme aiuto. Grazie! – arachnivore