Compartir tecnología

Programación principal de WIN32: operación de subprocesos (2) Exclusión mutua sincrónica

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Tabla de contenido

condición de carrera

Sección crítica

Mutex

Sección crítica y mutex

Semáforo

Eventoo


condición de carrera

  • En un entorno de subprocesos múltiples, cuando varios subprocesos acceden o modifican los mismos datos al mismo tiempo, el resultado final es el tiempo de ejecución del subproceso.

  • Si no existe un mecanismo de sincronización, se producirán condiciones de carrera, lo que puede provocar datos inexactos o excepciones del programa.

  1. #include <iostream>
  2. #include <windows.h>
  3. DWORD g_Num = 0;
  4. DWORD WINAPI WorkThread(LPVOID lp)
  5. {
  6. for (size_t i = 0; i < 10000000; i++)
  7. {
  8. //g_Num++;
  9. __asm LOCK INC [g_Num]
  10. }
  11. return 0;
  12. }
  13. int main()
  14. {
  15. HANDLE hThread[2] = { 0 };
  16. hThread[0] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
  17. hThread[1] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
  18. WaitForMultipleObjects(2, hThread, TRUE, -1);
  19. std::cout << g_Num << std::endl;
  20. return 0;
  21. }

Sección crítica

  1. #include <iostream>
  2. #include <windows.h>
  3. DWORD g_Num = 0;
  4. CRITICAL_SECTION cs = { 0 };
  5. DWORD WINAPI WorkThread(LPVOID lp)
  6. {
  7. for (size_t i = 0; i < 1000000; i++)
  8. {
  9. // 进入临界区
  10. EnterCriticalSection(&cs);
  11. // TODO
  12. g_Num++;
  13. // 退出临界区
  14. LeaveCriticalSection(&cs);
  15. }
  16. return 0;
  17. }
  18. int main()
  19. {
  20. HANDLE hThread[2] = { 0 };
  21. // 初始临界区
  22. InitializeCriticalSection(&cs);
  23. hThread[0] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
  24. hThread[1] = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
  25. WaitForMultipleObjects(2, hThread, TRUE, -1);
  26. std::cout << g_Num << std::endl;
  27. // 清理临界区
  28. DeleteCriticalSection(&cs);
  29. return 0;
  30. }

Mutex

  • Mutex (Mutex) se utiliza para evitar que varios subprocesos accedan o modifiquen recursos compartidos al mismo tiempo.

  • Solo un subproceso puede poseer el mutex al mismo tiempo. Si un subproceso toma posesión del mutex, otros subprocesos que soliciten el mutex se bloquearán hasta que se libere el permiso del mutex.

  • Crear un mutex - CreateMutex

  • Solicitar exclusión mutua: WaitForSingleObject

  • Liberar el mutex - ReleaseMutex

  1. #include <iostream>
  2. #include <windows.h>
  3. HANDLE hMutex = 0;
  4. DWORD g_Num = 0;
  5. DWORD WINAPI WorkThread(LPVOID lp)
  6. {
  7. for (size_t i = 0; i < 100000; i++)
  8. {
  9. WaitForSingleObject(hMutex, INFINITE);
  10. g_Num++;
  11. ReleaseMutex(hMutex);
  12. }
  13. return 0;
  14. }
  15. int main()
  16. {
  17. hMutex = CreateMutex(NULL, FALSE, NULL);
  18. HANDLE hThread1 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
  19. HANDLE hThread2 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
  20. WaitForSingleObject(hThread1, INFINITE);
  21. WaitForSingleObject(hThread2, INFINITE);
  22. CloseHandle(hThread1);
  23. CloseHandle(hThread2);
  24. CloseHandle(hMutex);
  25. std::cout << g_Num << std::endl;
  26. return 0;
  27. }

Sección crítica y mutex

  • sección crítica

    • Mecanismo de sincronización de subprocesos para recursos compartidos, las secciones críticas proporcionan acceso mutuamente exclusivo entre subprocesos del mismo proceso.

    • Cada subproceso debe poder ingresar a la sección crítica antes de acceder a los recursos compartidos y salir de la sección crítica después de completar el acceso para completar la sincronización del subproceso.

  • exclusión mutua

    • El mecanismo de sincronización de subprocesos se utiliza para restringir el acceso de varios subprocesos a recursos compartidos al mismo tiempo.

    • Los mutex pueden sincronizar procesos o subprocesos y pueden sincronizarse entre procesos.
      1. #include <iostream>
      2. #include <Windows.h>
      3. int main()
      4. {
      5. HANDLE hMutex = CreateMutex(NULL, FALSE, L"0xCC_Mutex");
      6. if (hMutex == NULL) return 0;
      7. if (GetLastError() == ERROR_ALREADY_EXISTS)
      8. {
      9. MessageBox(NULL, L"禁止多开", L"错误", MB_OKCANCEL);
      10. return 0;
      11. }
      12. std::cout << "Game Start..." << std::endl;
      13. system("pause");
      14. CloseHandle(hMutex);
      15. return 0;
      16. }
  • actuación

    • Las secciones críticas son más rápidas que las exclusiones mutuas dentro de subprocesos del mismo proceso.

  • Función

    • Los mutex se pueden sincronizar entre procesos, pero las secciones críticas solo se pueden sincronizar entre subprocesos del mismo proceso.

  • propiedad

    • Los mutex tienen estrictos requisitos de propiedad y solo los subprocesos con permisos mutex pueden liberarlos.

  • punto muerto
    • Cuando un hilo muere inesperadamente mientras mantiene un candado (excepción)
    • Si un subproceso termina inesperadamente mientras mantiene un bloqueo de sección crítica, el bloqueo no se liberará, lo que provocará que otros subprocesos que esperan la sección crítica no puedan ejecutarse normalmente, lo que resulta en un punto muerto.
      1. #include <iostream>
      2. #include <Windows.h>
      3. CRITICAL_SECTION CriticalSection = { 0 };
      4. DWORD WINAPI WorkThread(LPVOID lp)
      5. {
      6. EnterCriticalSection(&CriticalSection);
      7. printf("TID -> %d rn", GetCurrentThreadId());
      8. Sleep(5000);
      9. LeaveCriticalSection(&CriticalSection);
      10. return 0;
      11. }
      12. int main()
      13. {
      14. InitializeCriticalSection(&CriticalSection);
      15. HANDLE hThread1 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
      16. Sleep(1000);
      17. TerminateThread(hThread1, 1);
      18. HANDLE hThread2 = CreateThread(NULL, 0, WorkThread, NULL, 0, NULL);
      19. WaitForSingleObject(hThread2, INFINITE);
      20. DeleteCriticalSection(&CriticalSection);
      21. return 0;
      22. }
    • Si un subproceso termina inesperadamente mientras mantiene un bloqueo mutex, Windows libera automáticamente su propiedad para que otros subprocesos puedan continuar ejecutándose normalmente.
      1. #include <iostream>
      2. #include <Windows.h>
      3. HANDLE hMutex = NULL;
      4. DWORD WINAPI WorkThread1(LPVOID lp)
      5. {
      6. WaitForSingleObject(hMutex, INFINITE);
      7. printf("TID -> %d rn", GetCurrentThreadId());
      8. Sleep(5000);
      9. TerminateThread(GetCurrentThread(), -1);
      10. //todo
      11. return 0;
      12. }
      13. DWORD WINAPI WorkThread2(LPVOID lp)
      14. {
      15. printf("Wait For Thread1 Leavern");
      16. WaitForSingleObject(hMutex, INFINITE);
      17. printf("TID -> %d rn", GetCurrentThreadId());
      18. ReleaseMutex(hMutex);
      19. return 0;
      20. }
      21. int main()
      22. {
      23. hMutex = CreateMutex(NULL, FALSE, NULL);
      24. HANDLE hThread1 = CreateThread(NULL, 0, WorkThread1, NULL, 0, NULL);
      25. Sleep(1000);
      26. HANDLE hThread2 = CreateThread(NULL, 0, WorkThread2, NULL, 0, NULL);
      27. WaitForSingleObject(hThread2, INFINITE);
      28. CloseHandle(hMutex);
      29. CloseHandle(hThread1);
      30. CloseHandle(hThread2);
      31. return 0;
      32. }

Semáforo

  • Un semáforo es un objeto de sincronización que se utiliza para controlar el acceso a recursos compartidos por parte de múltiples subprocesos. Es un contador que representa la cantidad de recursos disponibles. Cuando el valor del semáforo es mayor que 0, indica que hay recursos disponibles; cuando el valor es 0, indica que no hay recursos disponibles.

    • esperar : Intenta disminuir el valor del semáforo. Si el valor del semáforo es mayor que 0, disminúyalo en 1 y continúe la ejecución. Si el valor del semáforo es 0, el hilo se bloquea hasta que el valor del semáforo sea mayor que 0.

    • liberado : Aumenta el valor del semáforo. Si hay otros hilos bloqueados esperando este semáforo, uno de ellos se despertará.

  • Crear semáforo

    • En sistemas Windows, utilice CreateSemaphore oCreateSemaphoreEx La función crea un semáforo.

  • Espere (Esperar) y suelte (Liberar) el semáforo

    • La espera de un semáforo generalmente se realiza usando WaitForSingleObject oWaitForMultipleObjects función.

    • Liberar el uso del semáforo ReleaseSemaphore función.

  1. #include <iostream>
  2. #include <Windows.h>
  3. #define MAX_COUNT_SEMAPHORE 3
  4. HANDLE g_SemapHore = NULL;
  5. HANDLE g_hThreadArr[10] = { 0 };
  6. DWORD WINAPI WorkThread(LPVOID lp)
  7. {
  8. WaitForSingleObject(g_SemapHore, INFINITE);
  9. for (size_t i = 0; i < 10; i++)
  10. {
  11. std::cout << "COUNT -> " << (int)lp << std::endl;
  12. Sleep(500);
  13. }
  14. ReleaseSemaphore(g_SemapHore, 1, NULL);
  15. return 0;
  16. }
  17. int main()
  18. {
  19. g_SemapHore = CreateSemaphore(
  20. NULL, //安全属性
  21. MAX_COUNT_SEMAPHORE, //初始计数
  22. MAX_COUNT_SEMAPHORE, //最大计数
  23. NULL //信号名称
  24. );
  25. if (g_SemapHore == NULL)
  26. {
  27. std::cout << GetLastError() << std::endl;
  28. return 1;
  29. }
  30. for (size_t i = 0; i < 10; i++)
  31. {
  32. g_hThreadArr[i] = CreateThread(
  33. NULL,
  34. 0,
  35. WorkThread,
  36. (LPVOID)i,
  37. 0,
  38. NULL
  39. );
  40. }
  41. WaitForMultipleObjects(10, g_hThreadArr, TRUE, INFINITE);
  42. //closehandle
  43. return 0;
  44. }

Eventoo

  • En la programación de Windows, los eventos son un mecanismo de sincronización que se utiliza para enviar señales entre múltiples subprocesos.El objeto del evento puede serreinicio manualoreinicio automático

    • Evento de reinicio manual: cuando se establece (señala) un evento, permanecerá en este estado hasta que se reinicie explícitamente. Esto significa que se pueden activar varios subprocesos que esperan el evento antes de que se restablezca el evento.

    • Evento de reinicio automático: cuando un hilo en espera recibe (señala) un evento, el sistema restablece automáticamente el estado del evento a no señalizado (sin señalizar). Esto significa que sólo se permite activar un hilo a la vez.

  • Crear evento

    • Uso de funciones API de WindowsCreateEventSe puede crear un objeto de evento.

    • lpEventAttributes: Puntero al atributo de seguridad, si está configurado enNULL, se utiliza la seguridad predeterminada.

    • bManualReset: siTRUE, se crea un evento de reinicio manual; de lo contrario, se crea un evento de reinicio automático.

    • bInitialState: siTRUE, entonces el estado inicial es el estado de la señal si;FALSE, es un estado de no señalización.

    • lpName: El nombre del evento.

  • Para configurar un evento (establecer el estado del evento al estado de la señal) useSetEventfunción

  • Para restablecer un evento (establecer el estado del evento en un estado no señalizado) utiliceResetEventfunción

  • Espere a que el evento espere a que un objeto de evento se convierta en un estado de señal.WaitForSingleObjectfunción

  1. #include <iostream>
  2. #include <Windows.h>
  3. DWORD WINAPI WorkThread(LPVOID lp)
  4. {
  5. HANDLE hEvent = *(HANDLE*)lp;
  6. std::cout << "Thread - " << GetCurrentThreadId() << " Waiting For Event" << std::endl;
  7. WaitForSingleObject(hEvent, INFINITE);
  8. std::cout << "Thread - " << GetCurrentThreadId() << " actived" << std::endl;
  9. return 0;
  10. }
  11. int main()
  12. {
  13. HANDLE hThreads[3] = { 0 };
  14. HANDLE hEvent = NULL;
  15. hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  16. if (hEvent == NULL) return 0;
  17. for (size_t i = 0; i < 3; i++)
  18. {
  19. hThreads[i] = CreateThread(NULL, 0, WorkThread, &hEvent, 0, NULL);
  20. }
  21. Sleep(2000);
  22. SetEvent(hEvent);
  23. WaitForMultipleObjects(3, hThreads, TRUE, INFINITE);
  24. CloseHandle(hEvent);
  25. return 0;
  26. }