La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

Desarrollador Profesional de Juegos Programación III Unidad II introducción a Mutex Secciones críticas.

Presentaciones similares


Presentación del tema: "Desarrollador Profesional de Juegos Programación III Unidad II introducción a Mutex Secciones críticas."— Transcripción de la presentación:

1 Desarrollador Profesional de Juegos Programación III Unidad II introducción a Mutex Secciones críticas

2 Threading en window- Mutex Temas Secciones críticas(critical data) Mutex CreateMutex() ReleaseMutex() CloseHandle()

3 Mutex Cuando creamos hilos, nos gustaría ser capaces de enviar datos entre ellos, después de todo, queremos mejorar el rendimiento manejando ciertos procesos en sus propios hilos. Esto normalmente significa que un hilo proporcionara datos a otro hilo. Para ello, necesitamos tener dos hilos que compartan la misma memoria, mientras un hilo pueda escribir los datos, y el otro hilo pueda leer estos datos. Esto en sí no es una cosa difícil de hacer, ya que cualquier función tiene acceso a los datos en el ámbito global. Dado que un hilo es sólo una función, los datos a los que una función tiene acceso los tendrá nuestro hilo también. Así que si lo que necesita un hilo para compartir los datos es compartir el scope, ¿cuál es el problema? ¿Qué sucedería si un subproceso intenta escribir los datos, al mismo tiempo que otro subproceso intenta leerlos? La respuesta es, obtendríamos datos corruptos. ¿Cómo podemos evitar esto, si ambos necesitan tener acceso a los mismos datos? Tendríamos que tener algún tipo de señal que le dijera a un hilo que el otro hilo los está utilizando. Si los datos están en uso, esperaremos hasta que se liberen, y si no están en uso, decirle al otro hilo que ahora los estamos utilizando. Estos datos se denominan sección crítica y la herramienta que se utilizará para indicar que la estamos usando se llama mutex.

4 Mutex Un mutex es una herramienta que va a "bloquear" una sección crítica cuando la estamos usando, y "desbloquear" cuando terminamos de usarla. Cuando queremos realizar un bloqueo, simplemente esperaramos hasta que el mutex no esté bloqueado (porque si esta bloqueado el mutex, significa que otro hilo está utilizando ahora la sección crítica), entonces nosotros mismos bloquearemos, y diremos a todos los demás que ahora estamos utilizando los datos. Sorprendentemente, ya hemos visto la función que utilizaremos para bloquear un mutex... esta en el modulo 1 el la que que espera a que el hilo llegue al final. WaitForSingleObject () hace exactamente eso. Vamos a utilizar tres funciones: – CreateMutex () para crear un mutex y obtener un identificador para el mutex. –WaitForSingleObject () esperará al mutex para que sea liberado o desbloqueado y después bloquearlo. –ReleaseMutex () dará a conocer o desbloquear un mutex que hemos bloqueado.

5 # include using namespace std; char buffer[128] = "\0"; HANDLE mutexHandle = NULL; bool threadStarted = false; void threadProc() { threadStarted = true; for (int i = 0; i < 10; i++) { WaitForSingleObject( mutexHandle, INFINITE); sprintf( buffer, "%s%d", buffer, i); Sleep(50); ReleaseMutex( mutexHandle); } Ejemplo de Mutex

6 void main() { mutexHandle = CreateMutex( NULL, false, NULL); HANDLE threadHandle; threadHandle = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) threadProc, NULL, 0, NULL); while (!threadStarted) { } for (int i = 0; i < 10; i++) { WaitForSingleObject( mutexHandle, INFINITE); cout << buffer << endl; Sleep(50); ReleaseMutex( mutexHandle ); } WaitForSingleObject( threadHandle, INFINITE); CloseHandle( mutexHandle); system("pause"); } Salida 0 01 012 0123 01234 012345 0123456 01234567 012345678 0123456789 Ejemplo de Mutex

7 Para este ejemplo, vamos a usar datos que ambos hilos van a compartir. Mientras un hilo llena una cadena con un texto y nuestro hilo principal espera hasta que la cadena está llena y luego continúe con su código. Este buffer es nuestro dato crítico, tanto el hilo principal y el threadProc van a leer y escribir en este buffer. Este buffer será nuestra sección critica, es el que vamos a proteger con nuestro mutex. char buffer[128] = "\0"; Entonces, no podemos proteger correctamente nuestros datos sin un mutex. Así que vamos a definir uno. Un mutex es un identificador al igual que nuestros threads. HANDLE mutexHandle = NULL; Este es el identificador que utilizaremos para hacer referencia a nuestro mutex de lectura/escritura a este buffer. Es lo que vamos a proteger con el mutex. Ejemplo de Mutex

8 Ahora, veamos algún concepto más. Cuando se crea un hilo, la computadora tiene que consumir un tiempo para establecer al proceso en el sistema operativo. Dependiendo de cómo se ha estructurado el hilo, esto podría llevar una cantidad considerable de tiempo. Debido a esto, es que en general se quiere saber exactamente cuándo han arrancado los hilos. Para esto hay un truco que usaremos. Para el hilo que vamos a manejar usaremos un flag que se establece en TRUE al comienzo del ThreadProc. La única manera que el flag sea verdadero, es que el ThreadProc comenzó la ejecución. Luego, simplemente tendrá que esperar en el hilo principal hasta que ese flag sea TRUE antes de continuar. Así que necesitamos un flag para indicar cuando comienza el thread. bool threadStarted = false; Ahora veremos el código de nuestro ThreadProc.

9 void threadProc() { Recordemos poner el flag en true para que el hilo principal sepa que ha comenzado. threadStarted = true; Ahora tomamos el buffer y le añadimos un número. Esto se va a hacer 10 veces. for (int i = 0; i < 10; i++) { Ahora la viene la parte interesante. Dado que estamos a punto de escribir datos en el buffer, es necesario hacer un bloqueo en el mutex. Una vez que tenemos el mutex bloqueado, nuestro hilo principal no será capaz de escribir o leer desde nuestro buffer. Podemos estar tranquilos, que nuestros datos están seguros. Vamos a bloquear el mutex con WaitForSingleObject (), la misma función que utilizamos para esperar a nuestro hilo finalizar visto en la unidad anterior. //bloqueo del mutex WaitForSingleObject(mutexHandle, INFINITE);

10 Ahora que tenemos el bloqueo en el mutex, vamos a escribir datos en nuestro buffer. Si usamos el contador i, deberíamos terminar con la cadena con el valor "0123456789“ Vamos a utilizar la función sprintf. Lo que estamos haciendo es añadir el valor de i en el final de nuestra cadena. Así que cuando i=0 nuestra cadena está vacía. Si se lo concatenamos al final de la cadena, será "0". Luego, cuando i = 1, nuestra cadena de será "01". Con i = 2, será igual a "012", y así sucesivamente. sprintf(buffer, "%s%d", buffer, i); // concatena i en el buffer Para poder ver las cosas más despacio, vamos a poner a dormir el hilo acá, así podremos observar mejor lo que pasa en la consola de salida. Sleep(50); // dormir 50 milisegundos Terminamos de escribir el buffer. Así que tenemos que liberar el mutex para que otros hilos puedan acceder al buffer. Esto se hace con la función ReleaseMutex(). El argumento de ReleaseMutex () es el mutex handle que queremos desbloquear. Así que pasemos como argumento nuestro mutex handle, y el hilo principal estará libre para acceder al buffer. ReleaseMutex(mutexHandle); // soltar el mutex }

11 void main() {// el thread principal cout << “Trabajando con Threads y Mutex's"; Antes de poner en marcha nuestro hilo, tenemos que crear el mutex. De lo contrario todas las llamadas que hemos hecho a nuestro mutex no harán nada, porque mutexHandle no contiene un mutex válido. Es importante hacerlo en el orden correcto también. Es fácil pasarse por alto cual es el lugar que se crea el mutex. Debe hacerse antes que el hilo empiece. Esto asegurará que nuestro mutex se creará antes de que alguien intente usarlo. Una vez que creemos nuestro hilo, es necesario que el mutex sea válido o puede haber algunos problemas. Vamos a crear nuestra mutex con CreateMutex (). Para nuestros propósitos, los parámetros no son importantes, para más datos sobre ellos ver: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createmutex.asp mutexHandle = CreateMutex( NULL, false, NULL); Tenemos el mutex listo para trabajar, y ya definimos nuestro threadProc, así que vamos a iniciar el hilo tal como lo hicimos en el segunda unidad. HANDLE threadHandle; threadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) threadProc, NULL, 0, NULL);

12 Ahora recordemos el flag de threadStarted, es el momento de usarlo. Dejemos que este hilo espere hasta que nuestro threadProc establezca este valor a true. Luego, continúe normalmente. Esto hará que el hilo espere hasta que nuestro hilo ha empezado a ejecutarse. (al final del la unidad comentaremos este while y veremos qué pasa) while ( !threadStarted) { } Ahora, nuestro hilo se está ejecutando, así que si no hacemos algo con nuestro buffer, nuestro hilo se va a llenar con los datos. Para simplificar, vamos a ver lo que el threadProc está haciendo. Imprimiremos al búfer dentro de threadProc. Pero recordemos, no podemos acceder a los datos en el mismo momento que threadProc, así que tenemos que proteger a nuestros buffer con el mutex. // vamos a imprimir 10 veces al igual que en threadProc for (int i = 0; i < 10; i++) { // bloqueamos el mutex para que threadProc no escriba en el buffer // mientras se está imprimiendo WaitForSingleObject( mutexHandle, INFINITE); cout << buffer << endl; // Imprimir el búfer. Sleep(50); // Al igual que en el threadProc, vamos a dormir este hilo así podemos // observar lo que está pasando. ReleaseMutex( mutexHandle ); / /cuando termina la impresión, liberar la exclusión mutua y permitimos // a threadProc agregue más datos al buffer. }

13 Esperamos a que nuestro hilo termine antes de salir de la aplicación WaitForSingleObject (threadHandle, INFINITE); Al igual que cualquier variable que creamos manualmente, nuestro sistema ha asignado recursos para nuestro mutex, tenemos que liberarlo con el fin de limpiar la memoria correctamente. Hacemos esto con CloseHandle (). El argumento es el mutexHandle en sí mismo. / / Limpiar nuestro mutex CloseHandle (mutexHandle); / / esperamos a una tecla para salir system("pause"); } La salida será Ahora es tiempo para probar. Primero, vamos a intentar cambiar los valores de sleep en los hilos. Cambiamos el "Sleep (50)" en el threadProc a "Sleep (40)." ¿Se ve un patrón en el que más de un número se inserta antes que el buffer se imprima? Probemos de otra forma. Ponemos sleep(50) en threadProc, y sleep(40) en el hilo principal. ¿Qué pasa ahora? El hilo principal termina de imprimir antes de threadProc, incluso termina con el buffer. Salida 0 01 012 0123 01234 012345 0123456 01234567 012345678 0123456789

14 "Entonces, ¿que es el lo importante de todo esto?" Lo que estamos tratando de demostrar es que no se puede depender de la velocidad en la que hilos se ejecutan. En este caso, tenemos suerte que a nuestros hilos de ejecución le lleve más o menos el mismo tiempo. Si ambos valores del sleep fueron 0, puede ocurrir que pasen cientos o miles de veces antes de ver a otro hilo poner más o menos datos en el buffer. Si cambiamos los valores de sleep podemos ver fácilmente esto. Vamos a comentar la instrucción CreateMutex () ahora. Lo que ocurrirá es que no se va a crear el mutex y por lo tanto cualquier llamada a WaitForSingleObject () o ReleaseMutex () no hará absolutamente nada. Esto permitirá a nuestros hilos ejecutarse sin tener que esperarse el uno del otro. Entonces nos gustaría ver que algo salio mal o diferente de que cuando lo hicimos de la forma correcta y protegimos los datos con los mutex. Bien, las posibilidades son pocas ya que cuando manipulamos hilos, es normal que todo siga bien. Esto se debe a que no estamos manipulando el buffer mucho. Además, lo estamos modificando en un solo lugar, en threadProc. Dado que todo lo que el hilo principal hace es la impresión del búfer, sería prácticamente imposible tener un problema. Pero que sucedería si tratamos de modificar los datos del búfer en el hilo principal?

15 Como ejercicio, escribir la función main () nuevamente para que se elimine un carácter a la vez desde el frente del buffer. Imprimir el contenido del búfer antes y después de quitar el carácter. Una vez modificado, apagar y prender el mutex y cambiar el valor del sleep. Con la combinación adecuada se puede encontrar una manera de romper el flujo de datos.


Descargar ppt "Desarrollador Profesional de Juegos Programación III Unidad II introducción a Mutex Secciones críticas."

Presentaciones similares


Anuncios Google