Estoy seguro de que se han hecho muchas preguntas similares antes, pero después de leer muchas de ellas todavía no estoy muy seguro de lo que debo hacer. Entonces, tengo un script de Python para controlar algunos instrumentos externos (una cámara y un medidor de potencia). He escrito clases para ambos instrumentos llamando a las funciones C en los archivos .dll usando ctypes. En este momento se ve algo como esto:
for i in range(10): power_reading = newport.get_reading(N=100,interval=1) # take power meter reading img = camera.capture(N=10) value = image_processing(img) # analyze the img (ndarray) to get some values results.append([power_reading,value]) # add both results to a list Quiero comenzar a ejecutar las dos primeras líneas al mismo tiempo. Tanto newport.get_reading como camera.capture tardan unos 100ms-1s en ejecutarse (se ejecutarán al mismo tiempo si elijo los argumentos correctos). No necesito que comiencen EXACTAMENTE al mismo tiempo, pero idealmente el retraso debe ser menor que aproximadamente el 10-20% del tiempo total de ejecución (por lo tanto, menos de 0,2 segundos de retraso cuando cada uno tarda aproximadamente 1 segundo en ejecutarse). Por lo que he leído, puedo usar el módulo de multiprocessing . Así que intento algo como esto basado en esta publicación :
def p_get_reading(newport,N,interval,return_dict): reading = newport.get_reading(N,interval,return_dict) return_dict['power_meter'] = reading def p_capture(camera,N,return_dict): img = camera.capture(N) return_dict['image'] = img for i in range(10): manager = multiprocessing.Manager() return_dict = manager.dict() p = multiprocessing.Process(target=p_capture, args=(camera,10)) p.start() p2 = multiprocessing.Process(target=p_get_reading, args=(newport,100,1)) p2.start() p.join() p2.join() print(return_dict)Tengo algunos problemas/preguntas:
Necesito obtener los valores de retorno de ambas llamadas a funciones. Usando mi método actual, return_dict solo muestra la entrada para capture_img pero no la lectura del medidor de potencia, ¿por qué? También leyó que puedo usar Queue , ¿cuál es el mejor método para mi propósito actual?
¿Cómo puedo saber si ambas funciones comienzan a ejecutarse al mismo tiempo? Estoy pensando en usar el módulo de time para registrar tanto el tiempo de inicio como el de finalización de ambas funciones, tal vez usando alguna función de contenedor para realizar el registro de tiempo, pero ¿el uso de multiprocessing planteará alguna restricción?
Por lo general, ejecuto mi código en un IDE (spyder) y, por lo que he leído, necesito ejecutar el símbolo del sistema para ver el resultado (tengo algunas declaraciones de impresión dentro de las funciones para fines de depuración). ¿Todavía puedo ejecutar en IDE para que ambas funciones se ejecuten al mismo tiempo?
El uso de un Lock puede ayudar con la sincronización:
import multiprocessing def p_get_reading(newport, N, interval, lock, return_dict): lock.acquire() lock.release() reading = newport.get_reading(N, interval) return_dict['power_meter'] = reading def p_capture(camera, N, lock, return_dict): lock.acquire() lock.release() img = camera.capture(N) return_dict['image'] = img if __name__ == "__main__": for i in range(10): manager = multiprocessing.Manager() return_dict = manager.dict() lock = multiprocessing.Lock() lock.acquire() p = multiprocessing.Process(target=p_capture, args=(camera,10,lock,return_dict)) p.start() p2 = multiprocessing.Process(target=p_get_reading, args=(newport,100,1,lock,return_dict)) p2.start() lock.release() p.join() p2.join() print(return_dict) Los dos objetos Process ahora se pueden crear e start() en cualquier orden ya que la rutina principal ya ha adquirido el bloqueo. Una vez liberados, los dos procesos lucharán entre sí para adquirir y liberar el bloqueo, y estarán listos casi al mismo tiempo.
Además, tenga en cuenta el uso de if __name__ == "__main__" ya que esto ayuda cuando el multiprocessing crea nuevos procesos.
Debo decir que esto parece un abuso de Lock
Una respuesta a su primera pregunta es simplemente no, si lo está haciendo de manera normal, pero sí, si así lo desea. No, porque la función de destino no puede comunicarse con el hilo de generación mediante un return . Una forma de hacerlo es usar una cola y funciones contenedoras de la siguiente manera:
from threading import Thread from Queue import Queue def p_get_reading(newport,N,interval,return_dict): reading = newport.get_reading(N,interval,return_dict) return_dict.update({'power_meter': reading}) return return_dict def p_capture(camera,N,return_dict): img = camera.capture(N) return_dict.update({'image': img}) return return_dict def wrapper1(func, arg1, arg2, queue): queue.put(func(arg1, arg2)) def wrapper2(func, arg1, arg2, arg3, queue): queue.put(func(arg1, arg2, arg3)) q = Queue() Thread(target=wrapper1, args=(p_capture, camera, 10 , q)).start() Thread(target=wrapper2, args=(p_get_reading, newport, 100, 1, q)).start() Ahora q contiene el dict actualizado y devuelto de p_capture() y p_get_reading() .