2013-08-15 14 views
10

Quindi, sto provando a scrivere un'applicazione che usa django come ORM, dal momento che entrambi dovranno fare un po 'di elaborazione dietro le quinte e un front-end facile da usare. La sua funzionalità principale sarà l'elaborazione dei dati presenti nel database, in un processo ad alta CPU (fondamentalmente simulazioni di monte carlo) e voglio implementare il multiprocessing, in particolare usando Pool (ho 4 processi). Fondamentalmente il mio codice viene eseguito in questo modo, con circa 20 bambini del genitore:Come gestire la concorrenza del database di multiprocessing Python, in particolare con django?

assorted import statements to get the django environment in the script 
from multiprocessing import Pool 
from random import random 
from time import sleep 

def test(child): 
    x=[] 
    print child.id 
    for i in range(100): 
     print child.id, i 
     x.append(child.parent.id) #just to hit the DB 
    return x 

if __name__ == '__main__': 
    parent = Parent.objects.get(id=1) 
    pool = Pool() 
    results = [] 
    results = pool.map(test,parent.children.all()) 
    pool.close() 
    pool.join() 
    print results 

con il codice in quanto tale, ottengo intermittente DatabaseError s o PicklingError s. I primi sono solitamente di tipo "database malformato" o "connessione persa con il server MySQL", questi ultimi di solito "non possono decapitare model.DoesNotExist". Sono casuali, si verificano con qualsiasi processo e, naturalmente, non c'è nulla di sbagliato nel DB stesso. Se imposto pool = Pool(proccesses=1) allora viene eseguito, in un singolo thread, bene. Inserisco anche varie dichiarazioni di stampa per assicurarmi che la maggior parte di esse sia effettivamente in esecuzione.

Ho anche stanno cambiando test a:

def test(child): 
    x=[] 
    s= random() 
    sleep(random()) 
    for i in range(100): 
     x.append(child.parent.id) 
    return x 

Il che fa solo ogni iterazione pausa meno di un secondo prima di eseguire, e rende tutto bene. Se ottengo l'intervallo casuale a circa 500 ms, inizia a comportarsi. Quindi, probabilmente un problema di concorrenza, giusto? Ma con solo 4 processi che colpiscono. La mia domanda è come posso risolvere questo senza fare grandi discariche di dati in anticipo? L'ho provato con SQLite e MySQL, ed entrambi hanno problemi con questo.

+0

Dal momento che i processi sono CPU-bound, allora perché non usi un 'multiprocessing.Lock' per evitare tutte le condizioni di gara nel database? – Bakuriu

risposta

7

Ok, quindi ho determinato (con l'aiuto di un amico) che il problema è che Django sta usando la stessa connessione di database per tutti i processi. Normalmente, quando si hanno richieste db simultanee, si trovano nello stesso thread (nel qual caso GIL esegue il comando) o si trovano su thread separati, nel qual caso django crea connessioni di database diverse. Ma con il multiprocessing, python crea deepcopies di tutto, quindi passa la stessa connessione al database ai sottoprocessi e poi si incastrano l'un l'altro fino a quando non si rompe.

La soluzione è di attivare una nuova connessione db da ciascun sottoprocesso (che è relativamente veloce).

from django import db 
... 
def sub_process(): 
    db.close_connection() 
    #the rest of the sub_process' routines 

#code that calls sub_process with the pool 

Sono andato avanti e indietro dall'avere quella linea, e non avendo quella linea, e sicuramente risolve tutto.

+1

Grazie! Stavo usando il threading in precedenza e ho avuto problemi quando ho iniziato a utilizzare il multiprocessing. La tua spiegazione tra i due casi ha reso la mia giornata. – Mikuz

3

In realtà ho recentemente ottenuto gli stessi problemi, e vedere questo post: Django multiprocessing and database connections ... e basta invocare l'operazione di chiusura connessione in sottoprocessi:

from django.db import connection 
connection.close() 
Problemi correlati