Multithreading
[Link]
💡 JOBS es el sistema para correr codigo multithreading con limitaciones
y seguridad dentro del entorno de unity, para tratar de hacerlo mas
seguro.
Es la manera de enlistar multiples trabajos para que se procesen en los diferentes
nucleos del procesador al mismo tiempo.
💡 Un un hilo (thread) es solo una unidad de ejecución dentro de un
proceso, no esta automaticamente relacionada a un nucleo del cpu,
aunque idealmente utilizaremos cada nucleo para correr un hilo. Si
generamos mas threads que nucleos tiene el procesador, el mismo
procesador se encarga de ponerlos a todos en una lista e ir
procesandolos cada vez que se libera un nucleo.
💡 En general vamos a intentar crear un pool general de trabajos a realizar,
para que cada nucleo pueda venir y buscar que tarea realizar a
continuacion cada vez que termine (no explicado en este resumen). El
sistema Jobs de Unity nos permine evitar eso, encargandose de
programar las tareas.
Sintaxis
La sintaxis basica es crear una instancia del tipo Thread en la que almaceno el
proceso que quiero realizar, luego con la funcion Start() lo comienzo (se asigna
automaticamente a un core libre). Con la funcion Join congelo el main Thread
hasta que la tarea haya terminado.
Multithreading 1
💡 En este ejemplo no estoy sacando partida del multithreading, ya que
no estoy haciendo nada en el main mientras la tarea se realiza, ni
realizando otras funciones en otros threads.
Listas y pools de Threads
La forma mas basica de crear un conjunto de threads que se ejecuten es
ponerlos a todos en un array.
C# tambien tiene una opcion que automatiza el comenzar los threads y esperar
a que terminen, que se llama ThreadPool. Esto pone todos los threads en una
Multithreading 2
bolsa y los va ejecutando cuando se liberan nucleos, y todo el proceso es
administrado por el runtime de .Net.
Con este tipo de sintaxis no guardamos una referencia al Thread, por lo que no
podemos saber exactamente cuando terminan de realizarse las tareas.
Riesgos (toda esta seccion se basa en las dependencias)
Race conditions
Se da cuando intentamos leer/escribir la misma informacion con distintos
threads. En el caso mas comun vamos a tener algun tipo de variable que
varios threads leen al principio de su funcion, luego realizan algo con el y la
sobreescriben. Esto hace que cada thread este trabajando sobre datos
incorrectos, ya que mientras corre no puede saber lo que estan haciendo
los demas.
Para evitar este tipo de problemas existe la palabra clave lock, que bloquea
un pedazo de codigo, haciendo que todo lo que este dentro ese ese bloque
no se pueda acceder por mas de un nucleo a la vez.
Multithreading 3
💡 Un bloqueo de todas las variables de los procesos genera que se
evite el risk condition, pero tambien hace que todos los procesos
se corran de a uno a la vez, evitando que se consiga una mejora
de performance atraves del multithreading. Por esto es importante
saber dividir bien el codigo, haciendo que solo lo estrictamente
necesario quede bloqueado, pero que todos los demas procesos
puedan correrse de forma paralela.
Deadlocks
Se dan cuando tenemos mas de un lock, pero uno requiere que el otro
termine para completarse, y viceversa. Por lo que el codigo queda
bloqueado ahi y nunca puede continuar.
Hard to debug
Cada vez que corramos nuestro codigo los nuecleos se van a asignar de
diferente manera, por lo que pueden aparecer bugs que solo se dan de vez
en cuando y que son dificiles de debugear.
Cost for overhead
Asignar cada proceso a un nucleo tambien tiene un costo de
procesamiento, por lo que si no asignamos tareas que convenga hacer de
forma paralela podemos terminar con un codigo que tarda mas
programando todas nuestras tareas multithreading que realmente
procesandolas.
Multithreading 4