1 / 48

Programando con Hilos de Windows*

Programando con Hilos de Windows*. Intel Software College. Objetivos. Al término de este módulo, será capaz de: Escribir programas para crear y terminar hilos Usar objetos de sincronización para coordinar la ejecución entre hilos y accesos a memoria. Agenda.

kairos
Download Presentation

Programando con Hilos de Windows*

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Programando con Hilos de Windows* Intel Software College

  2. Objetivos • Al término de este módulo, será capaz de: • Escribir programas para crear y terminar hilos • Usar objetos de sincronización para coordinar la ejecución entre hilos y accesos a memoria Programming with Windows Threads

  3. Agenda • Explorar las funciones del API para hilos en Win32 • Crear hilos • Esperar que los hilos terminen • Sincronizar acceso compartido entre hilos Prácticas para experiencia práctica Programming with Windows Threads

  4. Windows* tipo HANDLE • Cada objeto de windows es referenciado por variables de tipo HANDLE • Apuntador a objetos del kernel • Hilo, proceso, archivo, evento, mutex, semáforo, etc. • Las funciones para crear objetos devuelven un HANDLE • Los objetos se controlan a través de su HANDLE • No se manipulan directamente los objetos Programming with Windows Threads

  5. Creación de Hilos en Windows* HANDLE CreateThread( LPSECURITY_ATTRIBUTES ThreadAttributes, DWORD StackSize, LPTHREAD_START_ROUTINE StartAddress, LPVOID Parameter, DWORD CreationFlags, LPDWORD ThreadId ); // Out Programming with Windows Threads

  6. LPTHREAD_START_ROUTINE • CreateThread() espera un apuntador a una función global • Devuelve DWORD • Convención de llamadas de WINAPI • Un solo parámetro LPVOID (void *) • El hilo inicia la ejecución de la función DWORD WINAPI MyThreadStart(LPVOID p); Programming with Windows Threads

  7. Usando Hilos Explícitamente • Identifica porciones de código a paralelizar • Encapsula código en una función • Si el código ya es una función, una función de un driver puede necesitar ser escrita para coordinar el trabajo de varios hilos • Añadir la llamada CreateThread para asignar hilos para ejecutar la función Programming with Windows Threads

  8. Destruyendo Hilos • Libera recursos del SO • Limpia si se terminó el trabajo con el hilo antes de que el programa termine • La terminación del proceso lo hace • BOOL CloseHandle(HANDLE hObject); Programming with Windows Threads

  9. #include <stdio.h> #include <windows.h> DWORD WINAPI helloFunc(LPVOID arg ) { printf(“Hello Thread\n”); return 0; } main() { HANDLE hThread = CreateThread(NULL, 0, helloFunc, NULL, 0, NULL ); } Ejemplo: Creación de Hilos ¿Qué Sucede? Programming with Windows Threads

  10. Explicación del Ejemplo • El hilo principal es un proceso • Cuando el proceso termina, todos los hilos terminan • Se requiere algún método para esperar que un hilo termine Programming with Windows Threads

  11. #include <stdio.h> #include <windows.h> BOOL threadDone = FALSE ; DWORD WINAPI helloFunc(LPVOID arg ) { printf(“Hello Thread\n”); threadDone = TRUE ; return 0; } main() { HANDLE hThread = CreateThread(NULL, 0, helloFunc, NULL, 0, NULL ); while (!threadDone); } Esperando un Hilo de Windows* No es una buena idea! // ciclos desperdiciados! Programming with Windows Threads

  12. Esperando un Hilo • Espera un objeto (hilo) • DWORD WaitForSingleObject( • HANDLE hHandle, • DWORD dwMilliseconds ); • El hilo creador de hilos espera (bloqueado) hasta • El tiempo expira • Regresa un código para indicarlo • El hilo sale (se le indica al manejador) • Usa INFINITE para esperar hasta la terminación del hilo • No usa ciclos del CPU Programming with Windows Threads

  13. Esperando Muchos Hilos • Espera hasta 64 objetos (hilos) • DWORD WaitForMultipleObjects( • DWORD nCount, • CONST HANDLE *lpHandles, // arreglo • BOOL fWaitAll, // espera uno o todos • DWORD dwMilliseconds) • Espera a todos: fWaitAll==TRUE • Espera a cualquiera: fWaitAll==FALSE • El valor de retorno es el primer índice del arreglo encontrado Programming with Windows Threads

  14. Detalles en las funciones WaitFor* • Descriptor (Handle) como parámetro • Usado para diferentes tipos de objetos • Los objetos del kernel tienen dos estados • Signaled • Non-signaled • El comportamiento se define por el objeto referido por el manejador • Hilo: signaled indica terminado Programming with Windows Threads

  15. #include <stdio.h> #include <windows.h> const int numThreads = 4; DWORD WINAPI helloFunc(LPVOID arg ) { printf(“Hello Thread\n”); return 0; } main() { HANDLE hThread[numThreads]; for (int i = 0; i < numThreads; i++) hThread[i] = CreateThread(NULL, 0, helloFunc, NULL, 0, NULL ); WaitForMultipleObjects(numThreads, hThread, TRUE, INFINITE); } Ejemplo: Varios Hilos Programming with Windows Threads

  16. Activdad 1 - “HelloThreads” • Modifica el ejemplo previo para para mostrar • El mensaje “Hello Thread” apropiaido • Número de hilo único • Usa el la variable del ciclo for del CreateThread • Salida ejemplo: • Hello from Thread #0 • Hello from Thread #1 • Hello from Thread #2 • Hello from Thread #3 Programming with Windows Threads

  17. ¿Qué es Incorrecto? • ¿Qué se muestra en myNum? DWORD WINAPI threadFunc(LPVOID pArg) { int* p = (int*)pArg; int myNum = *p; printf( “Thread number %d\n”, myNum); } . . . // from main(): for (int i = 0; i < numThreads; i++) { hThread[i] = CreateThread(NULL, 0, threadFunc, &i, 0, NULL); } Programming with Windows Threads

  18. for(int i=0;i<numThreads;i++) { CreateThread(NULL, 0, threadFunc, &i, 0, NULL); } Contenido de la dirección 0x0001004 DWORD WINAPI threadFunc(LPVOID pArg) { int* p = (int*)pArg; int myNum = *p; printf( “Thread number %d\n”, myNum); } i=0x0001004 0 1 pArg=0x0001008 0x0001004 1 p=0x000100C mynum=0x0001010 1 Programming with Windows Threads

  19. Línea de Tiempo Hello “Threads” Programming with Windows Threads

  20. Condiciones de Concurso • Varios hilos acceden la misma variable de manera concurrente • Conflicto Lectura/Escritura • Conflicto Escritura/Escritura • El error más común en programas concurrentes • No siempre es obvio Programming with Windows Threads

  21. ¿Cómo Evitar Condiciones de Concurso? • El alcance de las variables local en los hilos • Variables declaradas dentro de las funciones de los hilos • Se almacenan en el stack del hilo • TLS (Thread Local Storage) • Controla el acceso compartido con regiones críticas • Exclusión mutua y sincronización • Lock, semáforo, evento, sección crítica, mutex … Programming with Windows Threads

  22. Solución – Almacenamiento “Local” DWORD WINAPI threadFunc(LPVOID pArg) { int myNum = *((int*)pArg); printf( “Thread number %d\n”, myNum); } . . . // from main(): for (int i = 0; i < numThreads; i++) { tNum[i] = i; hThread[i] = CreateThread(NULL, 0, threadFunc, &tNum[i], 0, NULL); } Programming with Windows Threads

  23. Windows* Mutexes • Objeto del kérnel que se referencía por un descriptor • “Signaled” cuando está disponible • Operaciones: • CreateMutex(…) // crear nuevo • WaitForSingleObject // wait y lock • ReleaseMutex(…) // unlock • Disponible entre procesos Programming with Windows Threads

  24. Sección Crítica en Windows* • Ligero, entre procesos solo mutex • El más útil y más usado • Nuevo tipo • CRITICAL_SECTION cs; • Operaciones de crear y destruir • InitializeCriticalSection(&cs) • DeleteCriticalSection(&cs); Programming with Windows Threads

  25. Sección Crítica en Windows* • CRITICAL_SECTIONcs ; • Intenta entrar al código protegido • EnterCriticalSection(&cs) • Se bloquea si otro hilo está en la sección crítica • Regresa cuando no hay hilos en la sección crítica • Al salir de la sección crítica • LeaveCriticalSection(&cs) • Debe ser desde el hilo que la obtiene Programming with Windows Threads

  26. #define NUMTHREADS 4 CRITICAL_SECTION g_cs; // ¿Por qué tiene que ser global? int g_sum = 0; DWORD WINAPI threadFunc(LPVOID arg ) { int mySum = bigComputation(); EnterCriticalSection(&g_cs); g_sum += mySum; // Los hilos acceden una a la vez LeaveCriticalSection(&g_cs); return 0; } main() { HANDLE hThread[NUMTHREADS]; InitializeCriticalSection(&g_cs); for (int i = 0; i < NUMTHREADS; i++) hThread[i] = CreateThread(NULL,0,threadFunc,NULL,0,NULL); WaitForMultipleObjects(NUMTHREADS, hThread, TRUE, INFINITE); DeleteCriticalSection(&g_cs); } Ejemplo: Sección Crítica Programming with Windows Threads

  27. f(x) = 1  4.0 dx =  (1+x2) 0 4.0 (1+x2) Ejemplo de Integración Numérica 4.0 static long num_steps=100000; double step, pi; void main() { int i; double x, sum = 0.0; step = 1.0/(double) num_steps; for (i=0; i< num_steps; i++){ x = (i+0.5)*step; sum = sum + 4.0/(1.0 + x*x); } pi = step * sum; printf(“Pi = %f\n”,pi); } 2.0 0.0 1.0 X Programming with Windows Threads

  28. Actividad 2 - Calculando Pi • Paraleliza la integración numérica usando hilos de Windows* • Como pueden las iteraciones de los ciclos dividirse entre los hilos • ¿Qué variables pueden ser locales? • ¿Qué variables necesitan ser visibles a todos los hilos? static long num_steps=100000; double step, pi; void main() { int i; double x, sum = 0.0; step = 1.0/(double) num_steps; for (i=0; i< num_steps; i++){ x = (i+0.5)*step; sum = sum + 4.0/(1.0 + x*x); } pi = step * sum; printf(“Pi = %f\n”,pi); } Programming with Windows Threads

  29. Eventos de Windows* • Usados para enviarle a otros hilos señales de que ha ocurrido un evento • Ejemplo: los datos están disponibles, el mensaje está listo • Los hilos esperan señales con la función WaitFor* • Dos tipos de eventos • Auto-reset • Manual-reset Programming with Windows Threads

  30. El evento permanece signaled hasta que un hilo espera y se libera Si no hay hilos esperando, el estado permanece signaled Una vez que el hilo se libera, el estado se restablece a not-signaled El evento permanece signaled hasta que es reiniciado por una llamada de la API Varios hilos pueden esperar y ser liberados Los hilos que originalmente están en espera pueden iniciar y ser liberados Tipos de Eventos Auto-reset Manual-reset Precaución: Sea cuidadoso cuando se usa WaitForMultipleObjects para esperar TODOS los eventos Programming with Windows Threads

  31. Creación de Eventos en Windows* • HANDLE CreateEvent( • LPSECURITY_ATTRIBUTES lpEventAttributes, • BOOL bManualReset, // TRUE => manual reset • BOOL bInitialState, // TRUE => begin signaled • LPCSTR lpName); // text name for object • Establecer bManualReset a TRUE para un evento “manual-reset event”; FALSE para un evento “auto-reset” • Establecer bInitialState a TRUE para que un evento inicie en estado “signaled”; FALSE para iniciar “unsignaled” Programming with Windows Threads

  32. Establecer y Reiniciar Eventos • Establecer un evento a un estado signaled • BOOL SetEvent( HANDLE event ); • Reiniciar un evento manualmente • BOOL ResetEvent( HANDLE event ); • Pulsar evento • BOOL PulseEvent( HANDLE event ); Programming with Windows Threads

  33. Ejemplo: Un Hilo Buscador • Un hilo creado busca un elemento • Manda una señal si el elemento se encuentra • El hilo principal (Main Thread) espera la señal y terminación del hilo • Muestra un mensaje si el elemento se encuentra • Muestra mensaje hasta la terminación del hilo • Ilustra • Usando el tipo HANDLE genérico • No esperar que cada objeto haga un signal Programming with Windows Threads

  34. Eejemplo: Eventos • DWORD WINAPI threadFunc(LPVOID arg) { • BOOL bFound = bigFind() ; • if (bFound) • { • SetEvent(hObj[0]); // señal, el dato fue encontrado • bigFound() ; • } • moreBigStuff() ; • return 0; • } HANDLE hObj[2]; // 0 es evento, 1 es hilo Programming with Windows Threads

  35. Ejempo: Función Principal • . . . • hObj[0] = CreateEvent(NULL, FALSE, FALSE, NULL); • hObj[1] = CreateThread(NULL,0,threadFunc,NULL,0,NULL); • /* Hacer otra cosa mientras el hilo realiza la búsqueda */ • DWORD waitRet = • WaitForMultipleObjects(2, hObj, FALSE, INFINITE); • switch(waitRet) { • case WAIT_OBJECT_0: // señal del evento • printf("found it!\n"); • WaitForSingleObject(hObj[1], INFINITE) ; • // • case WAIT_OBJECT_0+1: // señal del hilo • printf("thread done\n"); • break ; • default: • printf("wait error: ret %u\n", waitRet); • break ; • } • . . . Programming with Windows Threads

  36. Ejempo: Función Principal • . . . • hObj[0] = CreateEvent(NULL, FALSE, FALSE, NULL); • hObj[1] = CreateThread(NULL,0,threadFunc,NULL,0,NULL); • /* Do some other work while thread executes search */ • DWORD waitRet = • WaitForMultipleObjects(2, hObj, FALSE, INFINITE); • switch(waitRet) { • case WAIT_OBJECT_0: // señal del evento • printf(“encontrado!\n"); • WaitForSingleObject(hObj[1], INFINITE) ; • // fall thru • case WAIT_OBJECT_0+1: // señal del hilo • printf(“hilo terminado\n"); • break ; • default: • printf("wait error: ret %u\n", waitRet); • break ; • } • . . . Programming with Windows Threads

  37. Actividad 3 – Usando Eventos • Remplazar el spin-wait y la variable contador de hilos con eventos para enviar señal de terminación de hilo de la pieza computacional Programming with Windows Threads

  38. Semáforos de Windows* • Objetos de sincronización que contienen un contador • Representa el número de recursos disponibles • Formalizados por Edsger Dijkstra (1968) • Dos operaciones en los semáforos • Wait [P(s)]: El hilo espera hasta que s > 0, entonces s = s-1 • Post [V(s)]: s = s + 1 • El semáforo está en estado signaled si el contador > 0 Programming with Windows Threads

  39. Win32* Creación de Semáforos • HANDLE CreateSemaphore( • LPSECURITY_ATTRIBUTES lpEventAttributes, • LONG lSemInitial, // Initial count value • LONG lSemMax, // Maximum value for count • LPCSTR lpSemName); // text name for object • Valor de lSemMax debe ser 1 o mayor • Valor de lSemInitialdebe ser • Mayor o igual a cero, • Menor o igual que lSemMax,y • No puede estar fuera del rango Programming with Windows Threads

  40. Operaciones Wait y Post • Usa WaitForSingleObject para esperar en un semáforo • Si el contador es == 0, el hilo espera • Decrementa el contador en 1 cuando el contador > 0 • Incrementa el semáforo (Operación Post) • BOOL ReleaseSemaphore( • HANDLE hSemaphore, • LONG cReleaseCount, • LPLONG lpPreviousCount ); • Incrementa el contador del semáforo según el valor de cReleaseCount • Devuelve el valor previo del contador a través de lpPreviousCount Programming with Windows Threads

  41. Aplicaciones de los Semáforos • Controlar el acceso a estructuras de datos de tamaño limitado • Colas, stacks, deques • Usa un contador para enumerar elementos disponibles • Controla el acceso a un número finito de recursos • Descriptores de archivos, unidades de cinta, … • Regula la cantidad de hilos activos dentro de una región • Un semáforo binario [0,1] puede funcionar como un mutex Programming with Windows Threads

  42. Cuidados con el uso de Semáforos • No hay propietario del semáforo • Cualquier hilo puede liberar un semáforo, no solo el ultimo hilo que realizó el wait • Usar una buena práctica de programación para evitar esto • No existe el concepto de semáforo abandonado • Si el hilo termina antes de realizar la operación post, se pierde el incremento del semáforo • Deadlock Programming with Windows Threads

  43. Ejemplo: Semáforo como Mutex • El hilo principal abre el archivo de entrada, espera la terminación del hilo • Los hilos • Leen la línea del archivo de entrada • Cuentan todas las palabras de cinco letras en una línea Programming with Windows Threads

  44. Ejemplo: Principal HANDLE hSem1, hSem2; FILE *fd; int fiveLetterCount = 0; • main() • { HANDLE hThread[NUMTHREADS]; • hSem1 = CreateSemaphore(NULL, 1, 1, NULL); // Binary semaphore • hSem2 = CreateSemaphore(NULL, 1, 1, NULL); // Binary semaphore • fd = fopen(“InFile”, “r”); // Open file for read • for (int i = 0; i < NUMTHREADS; i++) • hThread[i] = CreateThread(NULL,0,CountFives,NULL,0,NULL); • WaitForMultipleObjects(NUMTHREADS, hThread, TRUE, INFINITE); • fclose(fd); • printf(“Number of five letter words is %d\n”, fiveLetterCount); • } Programming with Windows Threads

  45. Ejemplo: Semáforos • DWORD WINAPI CountFives(LPVOID arg) { • BOOL bDone = FALSE ; • char inLine[132]; int lCount = 0; • while (!bDone) • { • WaitForSingleObject(hSem1, INFINITE); // accede la entrada • bDone = (GetNextLine(fd, inLine) == EOF); • ReleaseSemaphore(hSem1, 1, NULL); • if (!bDone) • if (lCount = GetFiveLetterWordCount(inLine)) { • WaitForSingleObject(hSem2, INFINITE);//actualiza var • fiveLetterCount += lCount; • ReleaseSemaphore(hsem2, 1, NULL); • } • } • } Programming with Windows Threads

  46. Actividad 4 – Usando Semáforos • Usar semáforos binarios para controlar el acceso a variables compartidas Programming with Windows Threads

  47. Programando con Hilos de WindowsQue se ha Cubierto • Crear hilos para ejecutar trabajo encapsulado dentro de funciones • Lo normal de esperar hilos para terminar • Coordinar acceso compartido entre hilos para evitar condiciones de concurso • Almacenamiento local para evitar concursos • Objetos de sincronización para organizar el uso Programming with Windows Threads

  48. Programming with Windows Threads

More Related