Multiproceso en Python [con ejemplos de codificación]
Publicado: 2020-11-30Mejorar y hacer el código más rápido es el siguiente paso después de adquirir los conocimientos básicos de Python. Multithreading es una de esas formas de lograr esa optimización usando "Threads". ¿Qué son estos hilos? ¿Y en qué se diferencian de los procesos? Vamos a averiguar.

Al final de este tutorial, tendrá el conocimiento de lo siguiente:
- ¿Qué son los hilos y los procesos?
- ¿Cómo se logra Multithreading?
- ¿Cuáles son sus limitaciones?
- ¿Cuándo usar Multihilo?
Aprenda cursos de ciencia de datos en línea de las mejores universidades del mundo. Obtenga programas Executive PG, programas de certificados avanzados o programas de maestría para acelerar su carrera.
Tabla de contenido
Hilos en Python
Cuando pensamos en multitarea, pensamos en ejecución paralela. Multihilo no ejecución estrictamente paralela. Los subprocesos se pueden considerar como entidades separadas del flujo de ejecución de diferentes partes de su programa que se ejecutan de forma independiente. Básicamente, los subprocesos no se ejecutan en paralelo, pero Python cambia de un subproceso a otro tan rápido que parece que son paralelos.
Los procesos, por otro lado, son estrictamente paralelos y se ejecutan en diferentes núcleos para lograr una ejecución más rápida. Los subprocesos también se pueden ejecutar en diferentes procesadores, pero técnicamente no se ejecutarán en paralelo.
¿Y está pensando que si los subprocesos no se ejecutan en paralelo, entonces cómo hacen que las cosas sean más rápidas? La respuesta es que no siempre hacen que el procesamiento sea más rápido. Los subprocesos múltiples se usan específicamente en tareas en las que los subprocesos harán que el procesamiento sea más rápido.
Toda la información de un subproceso está contenida en el Bloque de control de subprocesos (TCB) . TCB consta de las siguientes partes principales:
- Un TID único: identificador de subprocesos
- Puntero de pila que apunta a la pila de subprocesos en el proceso
- Un contador de programa que almacena la dirección de la instrucción que el subproceso está ejecutando actualmente.
- Estado del subproceso (en ejecución, listo, esperando, iniciado o terminado)
Dicho esto, los procesos pueden contener múltiples subprocesos que comparten el código, los datos y todos los archivos. Y todos los subprocesos tienen su propio registro y pila separados a los que tienen acceso.
Ahora puede preguntarse, si los subprocesos usan los datos y el código comunes, ¿cómo pueden usarlos todos sin obstaculizar otros subprocesos? Esta es la mayor limitación de Multithreading, de la que hablaremos más adelante en este tutorial.
Cambio de contexto
Ahora, como se describió anteriormente, los subprocesos no se ejecutan en paralelo, sino en consecuencia. Entonces, cuando un subproceso T1 comienza a ejecutarse, todos los demás subprocesos permanecen en modo de espera. Solo después de que T1 haya terminado con su ejecución, cualquier otro subproceso en cola puede comenzar a ejecutarse. Python cambia de un hilo a otro tan rápido que parece una ejecución paralela. Este cambio es lo que llamamos 'Cambio de contexto'.
Programación multiproceso
Considere el siguiente código que usa subprocesos para realizar una operación de cubo y cuadrado.
| importar hilos def cubo (n) : imprimir( “Cubo: {}” .format(n * n * n)) def cuadrado (n) : imprimir( “Cuadrado: {}” .format(n * n)) si __nombre__ == “__principal__” : # crear el hilo t1 = enhebrado.Subproceso(objetivo=más cuadrado, argumentos=( 5 ,)) t2 = roscado.Hilo(objetivo=cubo, argumentos=( 5 ,)) # iniciar el hilo t1 t1.inicio() # iniciar el hilo t2 t2.inicio() # esperar hasta que se complete t1 t1.unirse() # esperar hasta que se complete t2 t2.unirse() # ambos hilos completados imprimir ( "¡Listo!" ) |
| #Producción: Cuadrado: 25 Cubo: 125 ¡Hecho! |
Ahora tratemos de entender el código.
Primero, importamos el módulo Threading que es responsable de todas las tareas. Dentro del main, creamos 2 subprocesos creando subclases de la clase Thread. Necesitamos pasar el objetivo, que es la función que debe ejecutarse en ese hilo, y los argumentos que deben pasarse a esas funciones.
Ahora, una vez que se declaran los subprocesos, debemos iniciarlos. Eso se hace llamando al método de inicio en los hilos. Una vez iniciado, el programa principal debe esperar a que los subprocesos terminen de procesarse. Usamos el método de espera para permitir que el programa principal haga una pausa y espere a que los subprocesos T1 y T2 terminen su ejecución.
Debe leer: Desafíos de Python para principiantes
Sincronización de subprocesos
Como discutimos anteriormente, los subprocesos no se ejecutan en paralelo, sino que Python cambia de uno a otro. Por lo tanto, existe una necesidad muy crítica de sincronización correcta entre los subprocesos para evitar cualquier comportamiento extraño.
Condición de carrera
Los subprocesos que están bajo el mismo proceso usan datos y archivos comunes que pueden conducir a una "Carrera" para los datos entre múltiples subprocesos. Por lo tanto, si varios subprocesos acceden a una parte de los datos, ambos subprocesos la modificarán y los resultados que obtendremos no serán los esperados. Esto se llama condición de carrera.
Entonces, si tiene dos subprocesos que tienen acceso a los mismos datos, ambos pueden acceder y modificarlos cuando se está ejecutando ese subproceso en particular. Entonces, cuando T1 comienza a ejecutarse y modifica algunos datos, T2 está en modo de suspensión/espera. Luego, T1 detiene la ejecución y entra en modo de suspensión, entregando el control a T2, que también tiene acceso a los mismos datos. Entonces, T2 ahora modificará y sobrescribirá los mismos datos, lo que generará problemas cuando T1 comience nuevamente.

El objetivo de la sincronización de subprocesos es asegurarse de que esta condición de carrera nunca se produzca y los subprocesos accedan a la sección crítica del código de uno en uno de forma sincronizada.
Cerraduras
Para resolver y prevenir la condición de carrera y sus consecuencias, el módulo de subprocesos ofrece una clase de bloqueo que utiliza semáforos para ayudar a sincronizar los subprocesos. Los semáforos no son más que banderas binarias. Considérelos como el letrero de “Ocupado” en las Cabinas Telefónicas que tienen el valor de “Ocupado” (equivalente a 1) o “No Ocupado” (Equivalente a 0). Entonces, cada vez que un subproceso se encuentra con un segmento de código con bloqueo, debe verificar si el bloqueo ya está en 1 estado. Si es así, tendrá que esperar hasta que se convierta en 0 para poder usarlo.
La clase Lock tiene dos métodos principales:
- adquirir([bloqueo]) : El método de adquisición toma el bloqueo de parámetros como Verdadero o Falso . Si se inició un bloqueo para un subproceso T1 con bloqueo como Verdadero, esperará o permanecerá bloqueado hasta que otro subproceso T2 bloquee la sección crítica del código. Una vez que el otro subproceso T2 libera el bloqueo, el subproceso T1 adquiere el bloqueo y devuelve True .
Por otro lado, si el bloqueo del subproceso T1 se inició con el bloqueo de parámetros como False , el subproceso T1 no esperará o permanecerá bloqueado si la sección crítica ya está bloqueada por el subproceso T2. Si lo ve bloqueado, inmediatamente devolverá False y saldrá. Sin embargo, si el código no fue bloqueado por otro hilo, adquirirá el bloqueo y devolverá True .
release () : cuando se llama al método de liberación en el bloqueo, desbloqueará el bloqueo y devolverá True. Además, verificará si hay subprocesos esperando que se libere el bloqueo. Si los hay, entonces permitirá que exactamente uno de ellos acceda a la cerradura.
Sin embargo, si el bloqueo ya está desbloqueado, se genera un ThreadError.
interbloqueos
Otro problema que surge cuando tratamos con múltiples bloqueos es: interbloqueos. Los interbloqueos ocurren cuando los subprocesos no liberan los bloqueos debido a varias razones. Consideremos un ejemplo simple donde hacemos lo siguiente:
| importar hilos l = enhebrar.Lock() # Antes de la 1ra adquisición l.adquirir() # Antes de la 2da adquisición l.adquirir() # Ahora adquirió el candado dos veces |
En el código anterior, llamamos al método de adquisición dos veces, pero no lo liberamos después de adquirirlo por primera vez. Por lo tanto, cuando Python vea la segunda declaración de adquisición, entrará en modo de espera indefinidamente ya que nunca liberamos el bloqueo anterior.
Estas condiciones de interbloqueo pueden colarse en su código sin que se dé cuenta. Incluso si incluye una llamada de liberación, su código puede fallar a mitad de camino y la liberación nunca se llamará y la cerradura permanecerá bloqueada. Una forma de superar esto es usando la declaración with – as , también llamada Administradores de contexto. Usando la declaración with – as , el bloqueo se liberará automáticamente una vez que el procesamiento finalice o falle por algún motivo.
Leer: Temas e ideas de proyectos de Python
Antes de que te vayas
Como discutimos anteriormente, Multithreading no es útil en todas las aplicaciones ya que realmente no hace que las cosas funcionen en paralelo. Pero la aplicación principal de Multithreading es durante las tareas de E/S, donde la CPU permanece inactiva mientras espera que se carguen los datos. Los subprocesos múltiples juegan un papel crucial aquí, ya que este tiempo de inactividad de la CPU se utiliza en otras tareas, lo que lo hace ideal para la optimización.
Si tiene curiosidad por aprender sobre ciencia de datos, consulte el Programa ejecutivo PG en ciencia de datos de IIIT-B y upGrad, creado para profesionales que trabajan y ofrece más de 10 estudios de casos y proyectos, talleres prácticos, tutoría con expertos de la industria, 1 -on-1 con mentores de la industria, más de 400 horas de aprendizaje y asistencia laboral con las mejores empresas.
¿Qué es un hilo en Python?
Los subprocesos son entidades dentro de un proceso que pueden programarse para su ejecución en Python. En términos sencillos, un hilo es un proceso de cálculo realizado por una computadora. Es un conjunto de tales instrucciones dentro de un programa que los desarrolladores pueden ejecutar independientemente de otros scripts. Los subprocesos le permiten aumentar la velocidad de la aplicación mediante el uso del paralelismo. Es un proceso ligero que permitirá que las tareas operen en paralelo. Los subprocesos funcionan de forma independiente y maximizan el uso de la CPU, por lo que mejoran el rendimiento de la CPU.
¿Cuál es el uso de subprocesos múltiples en Python?
Multithreading es una técnica de subprocesos en la programación de Python que permite que muchos subprocesos funcionen simultáneamente al cambiar rápidamente entre subprocesos con la ayuda de una CPU (llamado cambio de contexto). Cuando podemos dividir nuestra tarea en múltiples secciones separadas, utilizamos subprocesos múltiples. Por ejemplo, suponga que necesita realizar una consulta de base de datos compleja para obtener datos y dividir esa consulta en numerosas consultas individuales. En ese caso, será preferible asignar un hilo a cada consulta y ejecutarlas todas en paralelo.
¿Qué es la sincronización de subprocesos?
La sincronización de subprocesos se describe como un método que garantiza que dos o más procesos o subprocesos simultáneos no ejecuten una parte crucial de un programa simultáneamente. Los métodos de sincronización se utilizan para controlar el acceso de los procesos a las secciones importantes. Cuando iniciamos dos o más subprocesos dentro de un programa, existe la posibilidad de que varios subprocesos intenten acceder al mismo recurso, lo que genera resultados inesperados debido a problemas de concurrencia. Por ejemplo, si muchos subprocesos intentan escribir dentro del mismo archivo, los datos pueden dañarse porque uno de los subprocesos puede anular los datos, o cuando un subproceso se abre y otro subproceso cierra el mismo archivo.
