Los subprocesos múltiples y el procesamiento múltiple son las dos formas más comunes de lograr la concurrencia y la paralelización; sin embargo, no muchos desarrolladores entienden la diferencia entre ellos y no pueden elegir de manera efectiva cuál usar y cuándo.
En este artículo, discutiremos las diferencias entre Multithreading y Multiprocessing y cómo decidir qué usar y cómo implementarlo en Python.
Un hilo es un flujo de ejecución independiente. Básicamente, puede verse como un componente individual ligero de un proceso, que puede ejecutarse en paralelo. El subprocesamiento es una función que suele proporcionar el sistema operativo. Puede haber múltiples hilos en un proceso, que comparten el mismo espacio de memoria, lo que significa que comparten entre sí el código a ejecutar y las variables declaradas en el programa.
Para entender esto mejor, consideremos un ejemplo de los programas que se ejecutan en su computadora portátil en este momento. Probablemente esté leyendo este artículo con varias pestañas abiertas en su navegador. Mientras tanto, tienes la aplicación de escritorio de Spotify abierta para escuchar música. Ahora, el navegador y la aplicación de escritorio de Spotify son como dos procesos distintos que pueden emplear varios procesos o subprocesos para lograr el paralelismo. Por lo tanto, las diferentes pestañas de su navegador pueden ejecutarse en diferentes subprocesos. De manera similar, Spotify puede reproducir música usando un hilo y usar otro para descargar su canción favorita de Internet, y usar un tercero para mostrar la interfaz de usuario. Y esto se llama Multihilo.
Multithreading, como sugiere el nombre, es una tarea u operación que puede ejecutar varios subprocesos al mismo tiempo. Es una técnica popular que agiliza múltiples tareas en rápida sucesión al mismo tiempo y facilita el intercambio rápido y fácil de recursos entre múltiples subprocesos con el subproceso principal.
La siguiente imagen explica Multithreading en Python:
Python es un lenguaje lineal, pero podemos usar el módulo Threading Python para comprender e implementar el concepto de Multithreading en Python. El módulo de subprocesos ofrece una API intuitiva para generar fácilmente múltiples subprocesos que se pueden usar cuando se requiere más potencia de procesamiento.
Se puede utilizar como se muestra a continuación:
import threading from queue import Queue import time def testThread(num): print num if __name__ == '__main__': for i in range(5): t = threading.Thread(target=testThread, arg=(i,)) t.start()
En el fragmento de código anterior, target
se usa como objeto invocable, args
para pasar parámetros a la función y start
a iniciar el hilo.
Ahora, aquí viene algo interesante: la cerradura.
A menudo hay casos en la programación en los que le gustaría que sus subprocesos pudieran modificar o usar las variables que son comunes a los subprocesos. Sin embargo, para hacer esto, deberá usar algo conocido como Lock o Global Interpreter Lock (GIL) en Python.
de la pitón
En CPython, el bloqueo de intérprete global , o GIL , es un mutex que protege el acceso a los objetos de Python, evitando que varios subprocesos ejecuten códigos de bytes de Python a la vez. Este bloqueo es necesario principalmente porque la gestión de memoria de CPython no es segura para subprocesos.
A nivel de intérprete, Python básicamente serializa las instrucciones. Para que cualquier subproceso ejecute cualquier función, primero debe obtener un bloqueo global. Debido a que solo un subproceso puede obtener ese bloqueo a la vez, el intérprete finalmente debe ejecutar las instrucciones en serie. Esta arquitectura hace que la gestión de la memoria sea segura para subprocesos, pero no puede utilizar varios núcleos de CPU en absoluto.
En pocas palabras, cada vez que una función quiere usar o modificar una variable, bloquea esa variable de modo que si cualquier otra función quiere usar o modificar esa variable específica, tendrá que esperar hasta que se desbloquee esa variable.
Considere dos funciones que iteran una variable por uno. Puede usar el bloqueo para asegurarse de que una función pueda leer la variable, ejecutar cálculos y volver a escribir en ella antes de que otra función pueda hacerlo, de modo que podamos evitar la corrupción de datos.
La creación de subprocesos en Python es más útil para operaciones de E/S o tareas vinculadas a la red, como la ejecución de scripts, por ejemplo, en el caso del web scraping, en lugar de tareas que pueden requerir un uso intensivo de la CPU. Otro ejemplo es Tensorflow , que utiliza un grupo de subprocesos para transformar datos en paralelo.
Aparte de estas aplicaciones, las interfaces gráficas de usuario (GUI) utilizan subprocesos múltiples todo el tiempo para hacer que las aplicaciones respondan e sean interactivas. Un ejemplo común podría ser un programa de edición de texto en el que tan pronto como el usuario ingresa texto, se muestra en la pantalla. Aquí un subproceso se encarga de la entrada del usuario mientras que el otro subproceso maneja la tarea de mostrarlo. Podemos agregar más hilos para más funcionalidades, como revisión ortográfica, autocompletado, etc.
Ahora, habiendo discutido los hilos en detalle, pasemos a los procesos.
Un proceso es simplemente una instancia del programa de computadora que se está ejecutando. Cada proceso tiene su propio espacio de memoria que se utiliza para almacenar las instrucciones que se ejecutan y cualquier dato al que necesite acceder o almacenar para la ejecución del código. Debido a esto, generar un proceso requiere más tiempo y es más lento en comparación con un subproceso.
Como comentamos anteriormente, cuando ejecutamos varias aplicaciones en nuestro escritorio, cada aplicación es un proceso y cuando ejecuta estos procesos al mismo tiempo, se denomina Multiprocesamiento.
El multiprocesamiento es la capacidad de un procesador para ejecutar varias tareas no relacionadas simultáneamente. Le permite crear programas que pueden ejecutarse simultáneamente, omitiendo el bloqueo de intérprete global (GIL) y usar todo el núcleo de la CPU para una ejecución eficiente de las tareas.
Aunque el concepto de Multiprocesamiento es fundamentalmente diferente de Multithreading, su sintaxis o uso en Python es bastante similar. Similar al módulo Threading, tenemos un módulo Multiprocesamiento en Python que ayuda a generar diferentes procesos, donde cada proceso tiene su propio intérprete de Python y un GIL.
Dado que los procesos no comparten la misma memoria, no pueden modificar la misma memoria al mismo tiempo, lo que nos evita el riesgo de encontrarnos con un punto muerto o la posibilidad de corrupción de datos.
Se puede utilizar como se muestra a continuación:
import multiprocessing def spawn(num): print(num) if __name__ == '__main__': for i in range(5): p = multiprocessing.Process(target=spawn, args=(i,)) p.start() p.join() # this line allows you to wait for processes
Como también discutimos anteriormente, el procesamiento múltiple es una opción más inteligente en caso de que las tareas exijan mucha CPU y no tengan operaciones de E/S o interacciones del usuario.
Aquí hay algunos puntos para resumir las diferencias, ventajas y desventajas de Multiprocessing y Multithreading:
De esta discusión podemos sacar las siguientes conclusiones:
Ahora que comprende cómo funcionan Python Multiprocessing y Multithreading y cómo se comparan, puede escribir código de manera efectiva y aplicar los dos enfoques en una variedad de circunstancias.
Espero que hayas encontrado útil este artículo. ¡Sigue leyendo!