Innanzitutto, per rispondere alla domanda nel titolo: filter
è solo una funzione. Quindi, la sicurezza del thread si baserà sulla struttura dei dati con cui lo si utilizza.
Come già sottolineato nei commenti, le operazioni di lista stesse sono thread-safe in CPython e protette da GIL, ma questo è probabilmente solo un dettaglio di implementazione di CPython su cui non dovresti fare affidamento. Anche se potessi fare affidamento su di esso, la sicurezza di alcune delle operazioni probabilmente non significa il tipo di sicurezza del thread che intendi:
Il problema è che l'iterazione su una sequenza con filter
non è in generale un'operazione atomica. La sequenza potrebbe essere cambiata durante l'iterazione. A seconda della struttura dati sottostante al tuo iteratore, questo potrebbe causare effetti più o meno strani. Un modo per superare questo problema è iterando su una copia della sequenza creata con un'azione atomica. modo più semplice per fare questo per sequenze standard come tuple
, list
, string
è con l'operatore porzione simili:
filter(lambda x: x[0] == "in", l[:])
Oltre a questo non necessariamente thread-safe per altri tipi di dati, c'è un problema con questo però : è solo una copia superficiale. Dato che gli elementi della tua lista sembrano essere simili a una lista, un altro thread potrebbe parallelamente fare del l[1000][:]
per svuotare una delle liste interne (che sono indicate anche nella tua copia superficiale). Ciò renderebbe impossibile l'espressione del filtro con un IndexError
.
Detto questo, non è un peccato usare un lucchetto per proteggere l'accesso al tuo elenco e lo consiglierei sicuramente. A seconda del modo in cui i dati vengono modificati e del modo in cui si utilizzano i dati restituiti, potrebbe essere consigliabile eseguire una copia profonda degli elementi mantenendo il blocco e restituendole. In questo modo è possibile garantire che, una volta restituito, la condizione del filtro non cambierà improvvisamente per gli elementi restituiti.
Wrt. il tuo codice Logger
: Non sono sicuro al 100% su come prevedi di usarlo e se è fondamentale eseguire più thread su una coda e join
. Quello che mi sembra strano è che non usi mai lo Queue.task_done()
(supponendo che il suo self.log
sia un Queue
). Anche il tuo polling della coda è potenzialmente dispendioso. Se non è necessario il join
del filo, io suggerirei almeno trasformare l'acquisizione lock attorno:
class Logger(threading.Thread):
def __init__(self, log):
super(Logger, self).__init__()
self.daemon = True
self.log = log
self.data = []
self.data_lock = threading.Lock()
def run(self):
while True:
l = self.log.get() # thread will sleep here indefinitely
with self.data_lock:
self.data.append(l)
self.log.task_done()
def get_data(self, cond):
with self.data_lock:
d = filter(cond, self.data)
# maybe deepcopy d here
return d
Esternamente si potrebbe ancora fare log.join()
per assicurarsi che tutti gli elementi della coda log
vengono elaborati.
Si dovrebbe preoccuparsi della lista non il 'filtro' e no, le liste non sono thread-safe – thefourtheye
Fitler su una copia della lista. –
@thefourtheye http://stackoverflow.com/questions/6319207/are-lists-thread-safe contraddice la tua risposta un po ':) Credo che le liste stesse siano infallibili e il GIL protegga contro la corruzione dei dati in questo modo (* nella maggior parte delle situazioni *). –