El problema:Python multiprocesamiento y acceso a la base de datos con pyodbc "no es seguro"?
estoy recibiendo el siguiente rastreo y no entiendo lo que significa o cómo solucionarlo:
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 situación:
tengo obtuve una base de datos de SQL Server llena de datos para ser procesada. Intento utilizar el módulo de multiprocesamiento para paralelizar el trabajo y aprovechar los múltiples núcleos de mi computadora. Mi estructura de clases general es el siguiente:
- MyManagerClass
- Esta es la clase principal, donde se inicia el programa.
- crea dos objetos, uno multiprocessing.Queue
work_queue
y unowrite_queue
- También crea y pone en marcha los otros procesos, a continuación, espera a que termine.
- NOTA: esto es no una extensión de multiprocessing.managers.BaseManager()
- MyReaderClass
- Esta clase lee los datos de la base de datos de SQL Server.
- Pone elementos en el
work_queue
.
- MyWorkerClass
- Aquí es donde ocurre el proceso de trabajo.
- Obtiene elementos del
work_queue
y coloca los elementos completados en elwrite_queue
.
- MyWriterClass
- Esta clase es el encargado de escribir los datos procesados de nuevo a la base de datos de SQL Server.
- Obtiene elementos del
write_queue
.
La idea es que habrá un director, un lector, un escritor, y muchos trabajadores.
Otros detalles:
me sale el rastreo dos veces en stderr, así que estoy pensando que ocurre una vez para el lector y una vez por el escritor. Mis procesos de trabajo se crean bien, pero simplemente se sientan allí hasta que envíe un KeyboardInterrupt porque no tienen nada en el work_queue
.
Tanto el lector como el escritor tienen su propia conexión a la base de datos, creada en la inicialización.
Solución:
Gracias a Mark y Ferdinand Beyer para sus respuestas y preguntas que llevaron a esta solución. Con razón señalaron que el objeto Cursor no es "seleccionable", que es el método que usa el multiprocesamiento para pasar información entre procesos.
El problema con mi código era que MyReaderClass(multiprocessing.Process)
y MyWriterClass(multiprocessing.Process)
ambos conectados a la base de datos en sus __init__()
métodos. Creé ambos objetos (es decir, llamé su método init) en MyManagerClass
, y luego llamé al start()
.
por lo que sería crear los objetos de conexión y el cursor, y luego tratar de enviarlos al proceso hijo a través de la salmuera. Mi solución fue mover la creación de instancias de los objetos de conexión y cursor al método run(), que no se llama hasta que el proceso hijo esté completamente creado.
Solo para decir: excelente pregunta. – mavnn