La mejor respuesta
La respuesta a la pregunta tal como está planteada es: es la misma que la diferencia entre «uno «y» muchos «.
Pero creo que lo que probablemente quisiste decir es algo más parecido a esto: ¿en qué se diferencian las metodologías de programación para programar en un solo hilo en un solo proceso en comparación con varios hilos en un solo proceso?
Se han escrito libros sobre el tema. Pero muy brevemente, todas las diferencias se relacionan con un factor: cuando se programa en un solo hilo y proceso, siempre se sabe cómo se llegó a la instrucción actual. Las instrucciones ocurren en una sola secuencia. Pero al programar para muchos subprocesos, no tiene idea de qué instrucciones se ejecutaron y en qué orden para llegar a la instrucción actual. Esto hace que la programación sea más complicada.
Pero hay otro factor que lo complica: a diferencia de los procesos, los hilos comparten memoria. No sabe qué hilo tocó por última vez una ubicación de memoria determinada, o cuál será el siguiente, a menos que tenga algún tipo de «sincronización». De ahí la palabra clave «sincronizada» en Java (arrancada de Ada). Monitores, semáforos, bloqueos , las variables de condición e incluso el paso de mensajes se han utilizado para la sincronización.
Respuesta
(Actualización: tenga cuidado con otra respuesta que dice que no puede tener un servidor web de un solo subproceso que maneja bien las solicitudes concurrentes porque eso simplemente no es cierto.)
¿Por qué un servidor web multiproceso es mejor que un servidor de un solo hilo? No lo es.
Hay cuatro formas básicas en que un servidor web puede manejar la concurrencia:
- bifurcación un proceso de SO por solicitud (como versiones antiguas de Apache)
- generando un hilo de SO por solicitud (como una nueva versión de Apache)
- usando un bucle de eventos de un solo hilo (como nginx )
- usando hilos verdes o procesos ligeros programado por un tiempo de ejecución de VM en lugar del SO (como en Erlang)
Actualmente, los enfoques más comunes son el número 2 y 3.
Hay pros y contras de ambos de ellos. Para operaciones vinculadas a E / S (una característica de un servidor web típico), obtiene mejor rendimiento y mayor número de solicitudes simultáneas cuando usas un bucle de eventos de un solo subproceso . Pero el inconveniente es que debe usar E / S sin bloqueo exclusivamente asíncronas para todas las operaciones o, de lo contrario, bloqueará el bucle de eventos y perderá rendimiento. Por esa razón, es más fácil implementar un servidor de subprocesos múltiples pero paga en rendimiento.
Para operaciones vinculadas a la CPU (menos común para un servidor web habitual, tal vez más común para una API computacionalmente intensiva) es mejor tener un subproceso o proceso de SO por núcleo . Es fácil de hacer con bucles de eventos de un solo subproceso porque puede ejecutar un grupo de varios procesos, uno por núcleo. Es difícil hacerlo con servidores de subprocesos múltiples porque si la generación de subprocesos es su única forma de manejar solicitudes simultáneas, entonces no puede controlar realmente cuántos subprocesos tendrá, ya que no controla la cantidad de solicitudes. Una vez que tenga más subprocesos que el número de núcleos de CPU, perderá rendimiento para cambios de contexto y también utilizará mucha RAM.
Es por eso que un servidor nginx de un solo subproceso funciona mejor que un servidor web Apache de múltiples subprocesos (y es por eso que nginx se creó en primer lugar). También Redis , una base de datos conocida por su rendimiento excepcionalmente alto es de un solo subproceso .
Un ejemplo real que puedo darles es este: Mi primer servidor web fue Apache ejecutándose en una máquina Linux con 500 MB de RAM. Bifurcaba un nuevo proceso para cada solicitud (en realidad tenía un grupo, por lo que no había mucho bifurcación involucrada, pero tenía que mantener vivos esos procesos para reutilizarlos y eliminarlos de vez en cuando para evitar la pérdida de recursos).
Mi sistema operativo usó alrededor de 100 MB de RAM. Cada proceso de Apache utilizó 20 MB de RAM. Significaba que mi servidor solo podía manejar 20 solicitudes concurrentes y no había forma de evitarlo porque no tenía más RAM. La mayoría de los procesos estaban bloqueados en E / S, por lo que la utilización de la CPU era muy baja, cada solicitud por encima de esos 20 tenía que esperar y si esos 20 eran, por ejemplo descargas de larga duración, entonces mi servidor no respondía por completo.
Cuando se introdujo el servidor web nginx, utilizaba un bucle de eventos de un solo subproceso y no bloqueaba ninguna solicitud. Podría manejar muchas más solicitudes simultáneas, sin tener ningún problema con el mítico problema c10k: nginx se creó básicamente para resolver el problema c10k (10,000 solicitudes simultáneas).
Imagínese cuánta RAM se desperdicia para 10,000 hilos si incluso podría generar esa cantidad y cuánto tiempo se usa para cambios de contexto.
Uso de memoria de Apache multiproceso frente a nginx de un solo subproceso:
Por cierto, esta es la razón por la que Ryan Dahl usó una E / S sin bloqueo y un bucle de eventos de un solo subproceso en Node.js y todavía usa la misma idea en Deno, porque esa es la forma de escribir servidores de red de alto rendimiento (al contrario de lo que podría leer en otras respuestas aquí).