Utilice estas técnicas para ejecutar código simultáneamente y brindar una experiencia de usuario más fluida.

Conclusiones clave

  • La concurrencia y el paralelismo son principios fundamentales de la ejecución de tareas en informática, y cada uno tiene sus características distintas.
  • La concurrencia permite una utilización eficiente de los recursos y una mejor capacidad de respuesta de las aplicaciones, mientras que el paralelismo es crucial para un rendimiento y una escalabilidad óptimos.
  • Python proporciona opciones para manejar la concurrencia, como subprocesos y programación asincrónica con asyncio, así como paralelismo utilizando el módulo de multiprocesamiento.

La concurrencia y el paralelismo son dos técnicas que le permiten ejecutar varios programas simultáneamente. Python tiene múltiples opciones para manejar tareas de forma simultánea y en paralelo, lo que puede resultar confuso.

Explore las herramientas y bibliotecas disponibles para implementar correctamente la concurrencia y el paralelismo en Python, y en qué se diferencian.

Comprender la concurrencia y el paralelismo

La concurrencia y el paralelismo se refieren a dos principios fundamentales de ejecución de tareas en informática. Cada uno tiene sus características distintivas.

  1. concurrencia Es la capacidad de un programa para gestionar múltiples tareas al mismo tiempo sin necesariamente ejecutarlas exactamente al mismo tiempo. Gira en torno a la idea de entrelazar tareas, alternando entre ellas de una manera que parezca simultánea.
  2. Paralelismo, por otro lado, implica ejecutar múltiples tareas realmente en paralelo. Normalmente se aprovecha múltiples núcleos de CPU o procesadores. El paralelismo logra una verdadera ejecución simultánea, lo que le permite realizar tareas más rápido y es muy adecuado para operaciones computacionales intensivas.

La importancia de la concurrencia y el paralelismo

No se puede subestimar la necesidad de simultaneidad y paralelismo en la informática. He aquí por qué estas técnicas son importantes:

  1. Utilización de recursos: La simultaneidad permite la utilización eficiente de los recursos del sistema, lo que garantiza que las tareas progresen activamente en lugar de esperar ociosamente recursos externos.
  2. Sensibilidad: La concurrencia puede mejorar la capacidad de respuesta de las aplicaciones, especialmente en escenarios que involucran interfaces de usuario o servidores web.
  3. Actuación: El paralelismo es crucial para lograr un rendimiento óptimo, particularmente en tareas vinculadas a la CPU, como cálculos complejos, procesamiento de datos y simulaciones.
  4. Escalabilidad: Tanto la concurrencia como el paralelismo son esenciales para construir sistemas escalables.
  5. Preparado para el futuro: A medida que las tendencias de hardware sigan favoreciendo los procesadores multinúcleo, la capacidad de aprovechar el paralelismo será cada vez más necesaria.

Concurrencia en Python

Puede lograr simultaneidad en Python utilizando subprocesos y programación asincrónica con la biblioteca asyncio.

Enhebrado en Python

Threading es un mecanismo de concurrencia de Python que le permite crear y administrar tareas dentro de un solo proceso. Los subprocesos son adecuados para ciertos tipos de tareas, particularmente aquellas que están vinculadas a E/S y pueden beneficiarse de la ejecución simultánea.

pitón enhebrar módulo proporciona una interfaz de alto nivel para crear y gestionar subprocesos. Si bien GIL (Global Interpreter Lock) limita los subprocesos en términos de verdadero paralelismo, aún pueden lograr concurrencia entrelazando tareas de manera eficiente.

El siguiente código muestra un ejemplo de implementación de concurrencia utilizando subprocesos. Utiliza la biblioteca de solicitudes de Python para enviar una solicitud HTTP, una tarea común de bloqueo de E/S. También utiliza el módulo de tiempo para calcular el tiempo de ejecución.

import requests
import time
import threading

urls = [
'https://www.google.com',
'https://www.wikipedia.org',
'https://www.makeuseof.com',
]

# function to request a URL
defdownload_url(url):
response = requests.get(url)
print(f"Downloaded {url} - Status Code: {response.status_code}")

# Execute without threads and measure execution time
start_time = time.time()

for url in urls:
download_url(url)

end_time = time.time()
print(f"Sequential download took {end_time - start_time:.2f} seconds\n")

# Execute with threads, resetting the time to measure new execution time
start_time = time.time()
threads = []

for url in urls:
thread = threading.Thread(target=download_url, args=(url,))
thread.start()
threads.append(thread)

# Wait for all threads to complete
for thread in threads:
thread.join()

end_time = time.time()
print(f"Threaded download took {end_time - start_time:.2f} seconds")

Al ejecutar este programa, debería ver cuánto más rápidas son las solicitudes enhebradas que las solicitudes secuenciales. Aunque la diferencia es sólo una fracción de segundo, se obtiene una idea clara de la mejora del rendimiento cuando se utilizan subprocesos para tareas vinculadas a E/S.

Programación asincrónica con Asyncio

asincio proporciona un bucle de eventos que gestiona tareas asincrónicas llamadas corrutinas. Las corrutinas son funciones que puedes pausar y reanudar, lo que las hace ideales para tareas vinculadas a E/S. La biblioteca es particularmente útil para escenarios donde las tareas implican esperar recursos externos, como solicitudes de red.

Puede modificar el ejemplo anterior de envío de solicitudes para que funcione con asincio:

import asyncio
import aiohttp
import time

urls = [
'https://www.google.com',
'https://www.wikipedia.org',
'https://www.makeuseof.com',
]

# asynchronous function to request URL
asyncdefdownload_url(url):
asyncwith aiohttp.ClientSession() as session:
asyncwith session.get(url) as response:
content = await response.text()
print(f"Downloaded {url} - Status Code: {response.status}")

# Main asynchronous function
asyncdefmain():
# Create a list of tasks to download each URL concurrently
tasks = [download_url(url) for url in urls]

# Gather and execute the tasks concurrently
await asyncio.gather(*tasks)

start_time = time.time()

# Run the main asynchronous function
asyncio.run(main())

end_time = time.time()

print(f"Asyncio download took {end_time - start_time:.2f} seconds")

Usando el código, puede descargar páginas web simultáneamente usando asincio y aproveche las operaciones de E/S asincrónicas. Esto puede ser más eficiente que los subprocesos para tareas vinculadas a E/S.

Paralelismo en Python

Puedes implementar el paralelismo usando pitón multiprocesamiento módulo, que le permite aprovechar al máximo los procesadores multinúcleo.

Multiprocesamiento en Python

pitón multiprocesamiento El módulo proporciona una forma de lograr paralelismo mediante la creación de procesos separados, cada uno con su propio intérprete de Python y espacio de memoria. Esto evita efectivamente el bloqueo global de intérprete (GIL), lo que lo hace adecuado para tareas vinculadas a la CPU.

import requests
import multiprocessing
import time

urls = [
'https://www.google.com',
'https://www.wikipedia.org',
'https://www.makeuseof.com',
]

# function to request a URL
defdownload_url(url):
response = requests.get(url)
print(f"Downloaded {url} - Status Code: {response.status_code}")

defmain():
# Create a multiprocessing pool with a specified number of processes
num_processes = len(urls)
pool = multiprocessing.Pool(processes=num_processes)

start_time = time.time()
pool.map(download_url, urls)
end_time = time.time()

# Close the pool and wait for all processes to finish
pool.close()
pool.join()

print(f"Multiprocessing download took {end_time-start_time:.2f} seconds")

main()

En este ejemplo, multiprocesamiento genera múltiples procesos, lo que permite URL_descarga función para ejecutarse en paralelo.

Cuándo utilizar concurrencia o paralelismo

La elección entre concurrencia y paralelismo depende de la naturaleza de sus tareas y de los recursos de hardware disponibles.

Puede utilizar la concurrencia cuando trabaje con tareas vinculadas a E/S, como leer y escribir en archivos o realizar solicitudes de red, y cuando las limitaciones de memoria son una preocupación.

Utilice el multiprocesamiento cuando tenga tareas vinculadas a la CPU que puedan beneficiarse de un verdadero paralelismo y cuando tenga un aislamiento sólido entre tareas, donde el error de una tarea no debería afectar a otras.

Aproveche la concurrencia y el paralelismo

El paralelismo y la concurrencia son formas efectivas de mejorar la capacidad de respuesta y el rendimiento de su código Python. Es importante comprender las diferencias entre estos conceptos y seleccionar la estrategia más eficaz.

Python ofrece las herramientas y módulos que necesita para hacer que su código sea más efectivo a través de la concurrencia o el paralelismo, independientemente de si está trabajando con procesos vinculados a la CPU o a E/S.