¿Qué es la programación concurrente y por qué es importante entenderla?
La programación concurrente es una poderosa herramienta en el desarrollo de software moderno, permitiéndonos ejecutar múltiples tareas simultáneamente. Se torna especialmente crítica cuando necesitamos manipular o acceder a los mismos valores desde diferentes puntos de nuestro programa. Entender su importancia no solo optimiza rendimiento, sino que también evita problemas difíciles de detectar como condiciones de carrera. Estos problemas pueden surgir cuando se realizan operaciones concurrentes en una misma cuenta, por ejemplo, programando dos depósitos al mismo tiempo sobre un balance compartido.
¿Cómo afecta la concurrencia a las operaciones financieras?
Tomemos el ejemplo de un programa financiero encargado de gestionar depósitos y retiros en una cuenta bancaria. Supongamos que cada operación bancaria se maneja con una go rutina diferente, pero ambas rutinas acceden y modifican el mismo balance. Al ejecutar estas operaciones concurrentemente, surgen varios casos posibles:
Primero, el depósito de 100 seguido por uno de 200.
O primero el depósito de 200 seguido del de 100.
Y finalmente, ambos depósitos ocurriendo concurrentemente.
¿Cuál será el balance final? Si ambas operaciones se procesan al mismo tiempo, se podría terminar con un balance erróneo, como uno de 600 o 700, en lugar de 800, causando la desaparición de una transacción.
¿Qué es una condición de carrera y cómo nos afecta?
Una condición de carrera ocurre cuando múltiples operaciones concurrentes acceden y alteran el mismo recurso, resultando en estados inconsistentes e inesperados. Este fenómeno suele pasar desapercibido en su detección, pero sus efectos son potencialmente desastrosos, especialmente en sistemas que manipulan datos críticos, como en el caso de balance bancario. Si no se manejan adecuadamente, una transacción podría simplemente desaparecer, reflejando incorrectamente el saldo en la cuenta.
En este ejemplo, aunque el depósito y retiro parecen secuenciales, la concurrencia puede alterar el orden de ejecución, resultando en un balance inconsistente si el depósito no se procesa antes del retiro.
¿Cómo prevenir las condiciones de carrera en Go?
Para abordar estas complejidades, es crucial entender y poner en práctica técnicas que controlen el acceso concurrente a recursos compartidos:
Mutexes y Locks: Go proporciona herramientas para bloquear el acceso concurrente a recursos, asegurando que una función reciba el control exclusivo sobre un recurso antes de efectuar cambios.
Es fundamental explorar estas herramientas y construir programas robustos y confiables, evitando que los errores concurrentes comprometan la integridad de nuestros datos. La programación concurrente, aunque desafiante, es esencial para desarrollar sistemas rápidos y eficientes, y aprender a manejar estas condiciones es un valioso paso en tu carrera como desarrollador.
¡Continúa explorando y aprovechando el mundo de la programación concurrente! Cada nuevo avance que hagas en esta área fortalecerá tus habilidades y tus desarrollos futuros.
Este problema también es conocido como "Productor-Consumidor". Donde existen los principales involucrados:
Los productores: crean tareas y las ponen en un buffer compartido.
Los consumidores: sacan las tareas que el productor puso en el buffer compartido.
Buffer compartido: a través de este los productores y consumidores se comunican/envían tareas.
“Algo” que coordine el acceso al buffer; mejor conocidos como semáforos o mutex. Evitan que ocurra la condición de competencia (Race condition) por acceder al buffer.
En el ejemplo de Néstor:
Los productores: son nuestros depósitos.
Los consumidores: son nuestros retiros.
El buffer: Será nuestra cuenta bancaria.
Para evitar que algunos retiros o depósitos no se marquen o se marquen doble, necesitamos “algo” que coordine el acceso a nuestra cuenta bancaria.
Tremendo aporte!
Gracias bro
Hice este ejemplo con un wait group :D
package main
import("fmt""sync")var balance int
func Deposit(amount int, wg *sync.WaitGroup){ defer wg.Done() balance = balance + amount
fmt.Println("Deposited", amount)}func Withdraw(amount int, wg *sync.WaitGroup) bool { defer wg.Done()// Wait a second to simulate a delay//time.Sleep(time.Millisecond)if amount > balance { fmt.Println("Insufficient funds")returnfalse} balance = balance - amount
returntrue}func Balance() int {return balance
}func main(){var wg sync.WaitGroup balance =500 wg.Add(2) go Deposit(200,&wg) go Withdraw(700,&wg) wg.Wait() fmt.Println(Balance())}
Podrían tratarse estos temas en un curso general no ligado a ningún lenguaje de programación sino que se hable en general de todos estaría genial, me pregunto como sería esto en node... tengo miedo !!
Se vienen los problemas de concurrencia, vamos!
Y por que no mejor se hace sin concurrencia? cual es el motivo de la concurrencia en este ejemplo de los retiros y depósitos?
No hay un motivo especial para el ejemplo de retiros y depósitos. Es un ejemplo de concurrencia general donde se comparte una variable. Pero en un caso más realista se elige la concurrencia para ser más eficiente y ejecutar más operaciones en el mismo intervalo de tiempo. Si fuera un banco real tiene que maximizar las operaciones posibles para que sus clientes sientan que todo sucede en tiempo real, y que cuando les depositan ya tienen el dinero disponible para gastarlo.
Me gustaría que cambiaran el nombre de la clase a "La concurrencia de Schrödinger"
Siempre he creído que poner por defecto los Aportes y Preguntas en esta sección solo generan distracción en el alumno. Creo que debería ser opcional y si alguien los quiere mirar que sea por acción propia.
Puedes poner el vídeo en pantalla completa y no verás más que la clase :thinking: