2013-08-15 11 views
10

Tak więc, próbuję napisać aplikację, która używa django jako ORM, ponieważ będzie ona wymagała zarówno przetwarzania za kulisami, jak i łatwego w użyciu front-endu. Jego podstawową funkcjonalnością będzie przetwarzanie danych znajdujących się w bazie danych, w procesie wysokoprocesorowym (w zasadzie symulacje Monte Carlo) i chcę zaimplementować proces wieloprocesowy, w szczególności przy użyciu puli (otrzymuję 4 procesy). Zasadniczo mój kod działa jak ten, z około 20 dzieci dominującej:Jak obsługiwać współbieżność bazy danych wieloprocesorowych Pythona, w szczególności z 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 

z Kodeksem jako takiego, mam przerywany DatabaseError s lub PicklingError s. Te pierwsze mają zwykle postać "zniekształconej bazy danych" lub "utraconego połączenia z serwerem MySQL", te ostatnie są zwykle "nie mogą podnosić modelu.DoesNotExist". Są przypadkowe, występują w dowolnym procesie i oczywiście nie ma nic złego w samym DB. Jeśli ustawię pool = Pool(proccesses=1), to działa, w jednym wątku po prostu dobrze. Dodaję też różne instrukcje drukowania, aby upewnić się, że większość z nich faktycznie działa.

ja również się zmienia test do:

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

który właśnie sprawia, że ​​każda iteracja wstrzymać mniej niż sekundę przed uruchomieniem, a to sprawia, że ​​wszystko w porządku. Jeśli otrzymam losowy interwał w dół do około 500ms, zacznie działać. Więc prawdopodobnie problem współbieżności, prawda? Ale tylko 4 procesy uderzają. Moje pytanie brzmi: jak rozwiązać ten problem bez dokonywania dużych zrzutów danych z wyprzedzeniem? Przetestowałem go zarówno z SQLite, jak i MySQL, i oba mają z tym problem.

+0

Ponieważ procesy CPU-bound, to dlaczego czy nie używasz opcji 'multiplerocessing.Lock', aby uniknąć wszystkich warunków wyścigu do bazy danych? – Bakuriu

Odpowiedz

7

OK, więc postanowiłem (z pomocą przyjaciela), że problem polega na tym, że django używa tego samego połączenia z bazą danych dla wszystkich procesów. Zwykle, gdy masz równoczesne żądania db, są one w tym samym wątku (w którym to przypadku GIL kopie w) lub są na osobnych wątkach, w którym to przypadku django tworzy różne połączenia z bazą danych. Jednak w przypadku przetwarzania wieloprocesorowego pyton tworzy głębie wszystko, więc przekazuje to samo połączenie z bazą danych do podprocesów, a następnie przechodzą one na siebie nawzajem aż do zerwania.

Rozwiązanie polega na uruchomieniu nowego połączenia db z poziomu każdego podprocesu (który jest stosunkowo szybki).

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 

Udawaj się tam iz powrotem, nie mając tej linii i definitywnie naprawiaj wszystko.

+1

Dzięki! Używałem wcześniej wątków i miałem problemy, gdy zacząłem używać przetwarzania wieloprocesorowego. Twoje wyjaśnienie dwóch spraw zrobiło mój dzień. – Mikuz

3

Właściwie mam od niedawna ma te same problemy, i zobaczyć ten wpis: Django multiprocessing and database connections ... i po prostu wywołać operację zamknięcia połączenia w podprocesów:

from django.db import connection 
connection.close() 
Powiązane problemy