2012-01-09 20 views
7

Ho scritto circa 50 classi che uso per connettermi e lavorare con i siti web usando meccanize e threading. Funzionano tutti contemporaneamente, ma non dipendono l'uno dall'altro. Questo significa 1 sito web in classe 1 - 1 thread. Non è una soluzione particolarmente elegante, soprattutto per la gestione del codice, poiché molte code si ripetono in ogni classe (ma non abbastanza per trasformarlo in una classe per passare argomenti, poiché alcuni siti potrebbero richiedere un'ulteriore elaborazione dei dati recuperati nel mezzo di metodi - come "login" - che altri potrebbero non aver bisogno). Come ho detto, non è elegante, ma funziona. Inutile dire che accolgo con favore tutti i consigli su come scrivere meglio senza utilizzare 1 classe per ciascun approccio del sito. L'aggiunta di funzionalità aggiuntive o la gestione generale del codice di ogni classe è un'attività scoraggiante.Come ridurre l'utilizzo della memoria del codice Python filettato?

Tuttavia, ho scoperto che ogni thread richiede circa 8 MB di memoria, quindi con 50 thread in esecuzione stiamo considerando circa 400 MB di utilizzo. Se fosse in esecuzione sul mio sistema non avrei problemi con quello, ma dal momento che è in esecuzione su un VPS con solo 1 GB di memoria, sta iniziando a essere un problema. Puoi dirmi come ridurre l'utilizzo della memoria o ci sono altri modi per lavorare contemporaneamente con più siti?

Ho usato questo programma di prova rapido per verificare se sono i dati memorizzati nelle variabili della mia applicazione che sta utilizzando la memoria, o qualcos'altro. Come puoi vedere nel codice seguente, è solo l'elaborazione della funzione sleep(), ma ogni thread utilizza 8 MB di memoria.

from thread import start_new_thread 
from time import sleep 

def sleeper(): 
    try: 
     while 1: 
      sleep(10000) 
    except: 
     if running: raise 

def test(): 
    global running 
    n = 0 
    running = True 
    try: 
     while 1: 
      start_new_thread(sleeper,()) 
      n += 1 
      if not (n % 50): 
       print n 
    except Exception, e: 
     running = False 
     print 'Exception raised:', e 
    print 'Biggest number of threads:', n 

if __name__ == '__main__': 
    test() 

Quando si esegue questa operazione, il risultato è:

50 
100 
150 
Exception raised: can't start new thread 
Biggest number of threads: 188 

E rimuovendo running = False linea, posso quindi misurare memoria libera usando free -m comando nella shell:

   total  used  free  shared buffers  cached 
Mem:   1536  1533   2   0   0   0 
-/+ buffers/cache:  1533   2 
Swap:   0   0   0 

L'attuale calcolo perché so che ci vogliono circa 8 MB per thread è quindi semplice dividendo dividendo la differenza di memoria utilizzata prima e durante l'applicazione di test sopra è runnin g, diviso per i thread massimi che è riuscito a iniziare.

Probabilmente è solo allocata memoria, perché guardando top, il processo python utilizza solo circa lo 0,6% della memoria.

+0

Cosa sta occupando la memoria? Mi azzarderei a indovinare che sono i dati che stai estraendo dai siti. Se questo è il caso, probabilmente non c'è molto che potresti fare a meno di limitare il numero di thread in esecuzione. –

+0

Come si misura esattamente l'utilizzo della memoria? Direi che quegli 8 MB non sono realmente assegnati a ogni singolo thread. Una parte enorme di questi 8 MB può essere condivisa tra i thread (solo una supposizione ..)? – Frunsi

+0

Demian and frunsi, ho modificato la mia domanda per rispondere a entrambe le vostre domande. Grazie! – Gargauth

risposta

0

Non sono esperto di Python, ma forse ho alcuni pool di thread che controllano il numero totale di thread attivi e passano una "richiesta" a un thread una volta terminato con il thread precedente. La richiesta non deve essere l'oggetto thread completo, ma solo dati sufficienti per completare qualsiasi richiesta.

È anche possibile strutturarlo in modo che il pool di thread A con N thread esegua il ping del sito Web, una volta recuperati i dati, trasferirlo dai dati al pool di thread B con i thread Y che scricciano i dati.

2

L'utilizzo di "un thread per richiesta" è OK e facile per molti casi d'uso. Tuttavia, richiederà molte risorse (come hai sperimentato).

Un approccio migliore è quello di utilizzare uno asincrono, ma purtroppo è molto più complesso.

Alcuni indizi in questa direzione:

+0

Grazie, molto apprezzato. Ho letto di Twisted in passato, ma purtroppo non ne so molto e, a quanto pare, non sarei in grado di usare meccanizzare con esso. Darei un'occhiata se potessi far funzionare la meccanizzazione con asyncore. – Gargauth

+0

Dopo tutto, una soluzione "perfetta" sarebbe un mix di pool di thread con un thread per core della CPU (per utilizzarli per le attività di elaborazione) e IO asincrono. Una soluzione pratica dipenderà dal codice effettivo dell'applicazione. Forse, anche una semplice soluzione basata su 'select' lo farà per te. – Frunsi

+1

Ciò significa: nella tua discussione: invia un gruppo di richieste, quindi inserisci un ciclo che selezionerà le prese appropriate e gestirà tutti i dati in arrivo uno per uno ... e così via. Dopotutto, il SO si preoccupa comunque di socket IO, il tuo compito è quello di interfacciarsi con il sistema operativo nel modo più efficiente possibile. – Frunsi

1

La soluzione è quella di sostituire il codice come questo:

1) Fai qualcosa .
2) Aspetta che succeda qualcosa.
3) Fai qualcos'altro.

Con il codice come questo:

1) Fai qualcosa.
2) Disporre in modo che quando qualcosa accade, qualcos'altro viene fatto.
3) Fatto.

Da qualche altra parte, avete un paio di fili che fanno questo:

1) Attendere per qualsiasi cosa accada.
2) Gestisci ciò che è successo.
3) Vai al passaggio 1.

Nel primo caso, se stai aspettando che succedano 50 cose, hai 50 thread in attesa che succedano 50 cose. Nel secondo caso, hai un thread in attesa che farà qualunque di queste 50 cose devono essere fatte.

Quindi, non utilizzare una discussione per attendere che accada una sola cosa. Invece, organizzalo in modo che quando accade quella cosa, qualche altro thread farà tutto il necessario per essere fatto dopo.

Problemi correlati