Il problema:L'elaborazione multiprocessing e database Python con pyodbc "non è sicuro"?
Sto ottenendo il seguente traceback e non capisco che cosa significa e come risolvere il problema:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Python26\lib\multiprocessing\forking.py", line 342, in main
self = load(from_parent)
File "C:\Python26\lib\pickle.py", line 1370, in load
return Unpickler(file).load()
File "C:\Python26\lib\pickle.py", line 858, in load
dispatch[key](self)
File "C:\Python26\lib\pickle.py", line 1083, in load_newobj
obj = cls.__new__(cls, *args)
TypeError: object.__new__(pyodbc.Cursor) is not safe, use pyodbc.Cursor.__new__()
La situazione:
ho ha ottenuto un database SQL Server pieno di dati da elaborare. Sto cercando di usare il modulo multiprocessing per parallelizzare il lavoro e sfruttare i molteplici core sul mio computer. La mia struttura generale di classe è la seguente:
- MyManagerClass
- Questa è la classe principale, dove il programma si avvia.
- crea due oggetti, uno multiprocessing.Queue
work_queue
e unowrite_queue
- Inoltre, crea e lancia gli altri processi, quindi li attende per finire.
- NOTA: questo è non un'estensione multiprocessing.managers.BaseManager()
- MyReaderClass
- Questa classe legge i dati dal database di SQL Server.
- Inserisce elementi nello
work_queue
.
- MyWorkerClass
- Questo è dove il processo di lavorazione avviene.
- Ottiene elementi dallo
work_queue
e inserisce gli elementi completati nellowrite_queue
.
- MyWriterClass
- Questa classe si occupa di scrivere i dati elaborati al database di SQL Server.
- Ottiene elementi dallo
write_queue
.
L'idea è che ci sarà un manager, un lettore, uno scrittore, e molti lavoratori.
Altri particolari:
ho la traceback due volte in stderr, così sto pensando che succede una volta per il lettore e una volta per lo scrittore. I miei processi di lavoro vengono creati bene, ma restano seduti fino a quando non invio un KeyboardInterrupt perché non hanno nulla nello work_queue
.
Sia il lettore che lo scrittore dispongono di una propria connessione al database, creata durante l'inizializzazione.
Soluzione:
Grazie a Marco e Ferdinand Beyer per le loro risposte e le domande che hanno portato a questa soluzione. Giustamente hanno sottolineato che l'oggetto del Cursore non è "in grado di decollare", che è il metodo utilizzato dal multiprocessing per passare le informazioni tra i processi.
Il problema con il mio codice era che MyReaderClass(multiprocessing.Process)
e MyWriterClass(multiprocessing.Process)
erano entrambi collegati al database nei loro metodi __init__()
. Ho creato entrambi questi oggetti (cioè chiamato il loro metodo init) in MyManagerClass
, quindi chiamato start()
.
Quindi crea la connessione e gli oggetti del cursore, quindi prova a inviarli al processo figlio tramite pickle. La mia soluzione era spostare l'istanziazione della connessione e gli oggetti del cursore sul metodo run(), che non viene chiamato fino a quando il processo figlio non viene completamente creato.
Solo per dire: ottima domanda. – mavnn