Concurrencia y procesamiento asíncrono
Aprende cómo los backends manejan múltiples tareas de manera eficiente usando modelos de concurrencia y procesamiento asíncrono.
Por qué la concurrencia importa en los sistemas backend
Los sistemas backend modernos deben manejar muchas solicitudes entrantes al mismo tiempo; la concurrencia es lo que hace esto posible.
- Los servidores reciben solicitudes continuamente de múltiples usuarios y sistemas.
- Procesar una solicitud a la vez crearía retrasos severos y cuellos de botella.
- La concurrencia permite que los sistemas avancen en múltiples solicitudes sin esperar a que cada una termine por completo.
Detalles
En los sistemas reales, las solicitudes llegan continuamente y a menudo en grandes volúmenes, no una por una. Un servicio backend puede recibir cientos o miles de solicitudes por segundo, cada una requiriendo cómputo, acceso a la base de datos o llamadas a APIs externas.
Si el servidor procesara estas solicitudes estrictamente en secuencia, cada nueva solicitud tendría que esperar a que la anterior terminara. Esto crea una cola que crece rápidamente y provoca una latencia inaceptable para los usuarios.
El problema empeora porque muchas operaciones implican espera, como llamadas de red o acceso a disco. Durante este tiempo de espera, la CPU está inactiva, lo que es un uso ineficiente de los recursos.
La concurrencia aborda esto permitiendo que el sistema superponga trabajo. Mientras una solicitud espera por E/S, otra solicitud puede procesarse. Esto mantiene el sistema activo y mejora el rendimiento general.
El resultado es un sistema backend que puede manejar muchos usuarios a la vez mientras mantiene la capacidad de respuesta, lo cual es un requisito fundamental para las aplicaciones modernas.
Concurrencia vs Paralelismo
La concurrencia trata de gestionar múltiples tareas en progreso, mientras que el paralelismo trata de ejecutar múltiples tareas al mismo tiempo.
- La concurrencia permite que las tareas avancen alternando entre ellas.
- El paralelismo usa múltiples núcleos de CPU para ejecutar tareas simultáneamente.
Detalles
La concurrencia se centra en cómo un sistema estructura y gestiona múltiples tareas. Un servidor puede empezar a procesar una solicitud, pausarla mientras espera E/S y luego cambiar a otra solicitud. Las tareas avanzan por turnos, lo que mantiene el sistema eficiente incluso con recursos limitados.
El paralelismo, en cambio, depende del hardware. Si una máquina tiene múltiples núcleos de CPU, puede ejecutar varias tareas exactamente al mismo tiempo. Cada núcleo ejecuta su propia tarea de forma independiente, aumentando la capacidad total de procesamiento.
La distinción clave es que la concurrencia trata de coordinación y planificación, mientras que el paralelismo trata de ejecución simultánea. Resuelven problemas diferentes, pero a menudo se usan juntos.
Por ejemplo, un servidor backend podría gestionar miles de solicitudes concurrentes usando técnicas asíncronas, mientras también distribuye el trabajo entre múltiples núcleos de CPU para lograr ejecución paralela.
Entender esta diferencia es importante al razonar sobre el rendimiento del sistema, la escalabilidad y cómo funcionan bajo carga distintos frameworks de backend.
Hilos
Un hilo es una unidad de ejecución dentro de un proceso, y los servidores backend tradicionales usan múltiples hilos para manejar solicitudes de forma concurrente.
Cada carril ejecuta su propia ruta de ejecución. Más hilos significan más trabajo simultáneo, hasta que la CPU o la memoria se conviertan en el límite.
- Cada hilo representa un flujo de ejecución independiente dentro del servidor.
- Varios hilos permiten que el servidor maneje múltiples solicitudes al mismo tiempo.
- Los modelos basados en hilos se usan ampliamente en frameworks backend como Java Spring Boot.
Detalles
Un proceso es una instancia en ejecución de un programa, y dentro de ese proceso, los hilos son las unidades que realmente realizan el trabajo. Un solo proceso puede contener múltiples hilos, y cada uno se ejecuta de forma independiente mientras comparte el mismo espacio de memoria.
En los sistemas backend tradicionales, las solicitudes entrantes se asignan a hilos. Por ejemplo, cuando llega una solicitud, el servidor puede asignar un hilo de un pool de hilos para manejarla. Ese hilo procesa la solicitud de principio a fin, incluyendo la lógica de negocio, las consultas a la base de datos y la generación de la respuesta.
Este modelo es sencillo y fácil de entender. Cada solicitud tiene su propio hilo, por lo que los flujos de ejecución están aislados y los desarrolladores pueden escribir código de una forma mayormente secuencial.
Sin embargo, los hilos no son gratuitos. Cada hilo consume memoria y añade sobrecarga al sistema. A medida que crece el número de solicitudes concurrentes, crear demasiados hilos puede provocar una degradación del rendimiento debido al cambio de contexto y al agotamiento de recursos.
Debido a estas limitaciones, los sistemas modernos suelen combinar enfoques basados en hilos con técnicas asíncronas, pero entender los hilos sigue siendo esencial, ya que muchos sistemas en producción todavía dependen de este modelo.
Operaciones Bloqueantes vs No Bloqueantes
Las operaciones bloqueantes hacen que un hilo espere hasta que una tarea se complete, mientras que las operaciones no bloqueantes permiten que el hilo continúe haciendo otro trabajo.
Cuando una tarea espera I/O, un modelo bloqueante detiene al trabajador. Los sistemas no bloqueantes mantienen el carril activo mientras esperan.
- Las operaciones bloqueantes pausan la ejecución hasta que el resultado está listo.
- Las operaciones no bloqueantes permiten que el sistema continúe procesando otras tareas.
Detalles
En una operación bloqueante, un hilo inicia una tarea y luego espera hasta que esa tarea se complete por completo antes de continuar. Por ejemplo, cuando un hilo envía una consulta a la base de datos, puede quedar inactivo hasta que la base de datos responda. Durante este tiempo, el hilo no puede realizar ningún otro trabajo útil.
Esto se convierte en una limitación importante en sistemas que manejan muchas solicitudes. Si cada hilo pasa una cantidad significativa de tiempo esperando E/S, el servidor necesita más hilos para mantener el rendimiento, lo que aumenta el uso de recursos y la sobrecarga.
Las operaciones no bloqueantes toman un enfoque diferente. En lugar de esperar, el hilo inicia la tarea e inmediatamente continúa ejecutando otro trabajo. Cuando el resultado está listo, el sistema recibe una notificación y la tarea original puede reanudarse.
Esto permite que un solo hilo gestione múltiples tareas de forma eficiente, especialmente en cargas de trabajo intensivas en E/S donde el tiempo de espera domina el tiempo de ejecución.
Entender la diferencia entre el comportamiento bloqueante y no bloqueante es fundamental porque influye directamente en cómo se diseñan los sistemas backend para rendimiento, capacidad de respuesta y escalabilidad.
E/S asíncrona
La E/S asíncrona permite que los servidores sigan procesando otras tareas mientras esperan operaciones lentas como consultas a bases de datos o llamadas de red.
- Muchas operaciones de backend implican esperar a sistemas externos como bases de datos o APIs.
- La E/S asíncrona evita que los hilos permanezcan inactivos durante estas esperas.
- Este enfoque mejora el rendimiento y la eficiencia de los recursos.
Detalles
En los sistemas de backend, muchas operaciones no están limitadas por la CPU, sino por la E/S. Tareas como consultar una base de datos, llamar a una API externa o leer desde disco pueden tardar mucho más que un cálculo en memoria.
Si estas operaciones se manejan de forma bloqueante, el hilo permanece inactivo mientras espera el resultado, lo que desperdicia recursos del sistema y limita cuántas solicitudes puede manejar el servidor.
La E/S asíncrona resuelve esto permitiendo que el servidor inicie una operación y luego continúe con otro trabajo en lugar de esperar. El sistema lleva un seguimiento de la operación pendiente y reanuda el procesamiento una vez que el resultado está disponible.
Esto permite que un solo hilo o un pequeño número de hilos gestione muchas solicitudes concurrentes de forma eficiente, convirtiendo la E/S asíncrona en una técnica fundamental en sistemas de backend de alto rendimiento.
Modelo de bucle de eventos
El modelo de bucle de eventos usa un pequeño número de hilos para manejar muchas solicitudes procesando tareas de forma asíncrona en lugar de dedicar un hilo por solicitud.
Cada solicitud ocupa su propio trabajador.
Un bucle recorre muchas tareas.
El bucle de eventos delega el trabajo pesado a hilos.
- Un único bucle de eventos procesa continuamente las tareas de una cola.
- Las operaciones no bloqueantes permiten que el sistema evite esperar.
- Las callbacks se ejecutan cuando las operaciones asíncronas se completan.
Detalles
En el modelo de bucle de eventos, el sistema no asigna un hilo separado a cada solicitud. En su lugar, un bucle central comprueba continuamente si hay nuevas tareas y las ejecuta una por una. Cuando llega una solicitud, el bucle de eventos comienza a procesarla e inicia cualquier operación de E/S necesaria de forma no bloqueante.
En lugar de esperar a que estas operaciones se completen, el bucle de eventos pasa a atender otras solicitudes entrantes. Esto mantiene el sistema ocupado y evita perder tiempo esperando sin hacer nada, algo común en los modelos basados en hilos.
Cuando una operación asíncrona termina, su resultado se coloca en una cola de callbacks. El bucle de eventos finalmente toma esta callback y completa el trabajo restante de esa solicitud.
Un ejemplo muy conocido de este modelo es Node.js. Usa un bucle de eventos de un solo hilo combinado con E/S asíncrona para manejar miles de conexiones concurrentes de forma eficiente, apoyándose en callbacks y promises para gestionar el flujo de ejecución.
Este enfoque es muy eficiente para cargas de trabajo intensivas en E/S, pero requiere un diseño cuidadoso para evitar bloquear el bucle de eventos, ya que una sola tarea lenta puede retrasar a todas las demás.
Condiciones de carrera
Una condición de carrera ocurre cuando varios hilos acceden y modifican datos compartidos al mismo tiempo, lo que provoca resultados impredecibles e incorrectos.
- Las condiciones de carrera ocurren cuando varios hilos operan sobre datos compartidos sin la coordinación adecuada.
- El resultado final depende del momento y del orden de ejecución.
- Son una fuente importante de errores en sistemas concurrentes.
Detalles
Una condición de carrera surge cuando dos o más hilos leen y actualizan la misma parte de datos al mismo tiempo. Como estas operaciones no están sincronizadas, el resultado depende de qué hilo se ejecuta primero, lo que hace que el comportamiento del sistema sea impredecible.
Por ejemplo, si dos hilos leen el mismo saldo de una cuenta y ambos intentan actualizarlo, una actualización puede sobrescribir a la otra. Esto produce datos incorrectos, aunque cada operación individual parezca correcta por separado.
Estos problemas son difíciles de detectar porque pueden no ocurrir de forma consistente. Pequeñas diferencias de tiempo pueden cambiar el orden de ejecución, causando errores que son difíciles de reproducir y depurar.
Para evitar las condiciones de carrera, los sistemas usan técnicas de sincronización como bloqueos, operaciones atómicas y transacciones de base de datos. Estos mecanismos garantizan que solo un hilo pueda modificar datos compartidos a la vez o que las operaciones se ejecuten de forma segura sin interferencias.
Sección de preguntas
1 / 5
Esta lección forma parte del contenido premium
Pásate al plan premium para eliminar el desenfoque y desbloquear la lectura completa.