2014-09-11 13 views
8

Sto usando pandas su un server web (apache + modwsgi + django) e ho un bug difficile da riprodurre che ora ho scoperto è causato dal fatto che i panda non sono thread-safe.panda e sicurezza del thread numpy

Dopo un sacco di riduzione del codice ho finalmente trovato un breve programma standalone che può essere utilizzato per riprodurre il problema. Puoi vederlo sotto.

Il punto è: contrariamente alla risposta di this question questo esempio mostra che i panda possono bloccarsi anche con operazioni molto semplici che non modificano un dataframe. Non riesco a immaginare come questo semplice frammento di codice possa non essere sicuro con i thread ...

La domanda riguarda l'utilizzo di panda e numpy in un server web. È possibile? Come posso risolvere il mio codice usando i panda? (Un esempio di utilizzo di blocco sarebbe utile)

Ecco il codice che causa un errore di segmentazione:

import threading 
import pandas as pd 
import numpy as np 

def let_crash(crash=True): 
    t = 0.02 * np.arange(100000) # ok con 10000                    
    data = pd.DataFrame({'t': t}) 
    if crash: 
     data['t'] * 1.5 # CRASH 
    else: 
     data['t'].values * 1.5 # THIS IS OK! 

if __name__ == '__main__': 
     threads = [] 
     for i in range(100): 
      if True: # asynchronous                       
       t = threading.Thread(target=let_crash, args =()) 
       t.daemon = True 
       t.start() 
       threads.append(t) 
      else: # synchronous                        
       let_crash() 
     for t in threads: 
      t.join() 

mio ambiente: python 2.7.3, 1.8.0 NumPy, panda 0.13.1

+1

Non va in crash per me. Python 2.7.6, numpy 1.8.2, panda 0.14.1. Ho provato il ciclo principale fino a '10000'. – osa

risposta

3

vedere avvertenza nei documenti qui: http://pandas.pydata.org/pandas-docs/dev/gotchas.html#thread-safety

panda non è thread-safe perché il meccanismo di copia sottostante non lo è. Numpy credo abbia un'operazione di copia atomica, ma i panda hanno uno strato sopra questo.

Copy è la base delle operazioni panda (come la maggior parte delle operazioni generano un nuovo oggetto per tornare per l'utente)

Non è banale per risolvere questo problema e sarebbe venuto con un costo perf piuttosto pesante così avrebbe bisogno di un po ' di lavoro per affrontarlo correttamente.

Il più semplice è semplicemente non condividere oggetti tra thread o bloccarli durante l'utilizzo.

+1

Ma nessun oggetto viene condiviso, i suoi DataFrames sono locali per ogni thread ... Questo sembra terribilmente simile a [questo] (https://github.com/numpy/numpy/issues/4642) e la soluzione che doveva essere più attento a rilasciare GIL, vedere [qui] (https://github.com/numpy/numpy/pull/4648). Sei sicuro di non rilasciare GIL da qualche parte dove è necessaria una chiamata a una funzione API Python? – Jaime

+0

no - questi coinvolgono tutti numpy/Numexpr (nessun codice c o cython coinvolto qui) quindi potrebbe esserci un problema lì – Jeff

+0

In realtà, il loro IS è un codice cython coinvolto, ma nell'accesso alla colonna. '' data ['t'] '' per vari tipi di controllo/indicizzazione. Dovrebbe essere thread sicuro però. – Jeff

0

Configura mod_wsgi per l'esecuzione in una modalità a thread singolo.

WSGIDaemonProcess mysite processes=5 threads=1 
WSGIProcessGroup mysite 
WSGIApplicationGroup %{GLOBAL} 

In questo caso si utilizza la modalità mod_wsgi demone modo che processi/thread possono essere impostati indipendentemente su qualunque Apache MPM in uso.

+0

Ho già provato questa soluzione, ma thread = 1 rende il server bloccato quando alcune richieste richiedono molto tempo per essere pubblicate. –

+1

I processi forniscono la concorrenza in quel caso e il motivo per cui si dispone di più di un processo. Quanti processi hai effettivamente specificato? Qual è il tempo medio di esecuzione delle tue richieste e qual è il tuo throughput? Devi sapere che sono in grado di fornire una capacità adeguata. Se solo gli URL specifici presentano questo problema con il multithreading, è possibile suddividere verticalmente l'applicazione in più gruppi di processi daemon mod_wsgi e bloccare gli URL non sicuri nei processi a thread singolo. Vedere il seguente messaggio: http://blog.dscpl.com.au/2014/02/vertically-partitioning-python-web.html –

+0

Questo è interessante. Ero convinto che thread = 1 fosse la cosa sbagliata da fare ... Non so dove cercare il numero di processi, ho la configurazione di default di apache. Ma pstree mi dice che ce ne sono molti (oltre 100). Il problema è con i caricamenti di file ... quando un client avvia un caricamento di file (che può richiedere alcuni minuti) il server diventa irresponsabile. Ho pensato che esistesse una correlazione con la transazione postgresql e ho provato a usare manual-commit e cose del genere, ma senza successo. –