Condivisione della tecnologia

Programmazione di base WIN32 - Operazione thread (2) Esclusione reciproca sincrona

2024-07-12

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

Sommario

condizione di gara

Sezione critica

Mutex

Sezione critica e mutex

Semaforo

Eventoo


condizione di gara

  • In un ambiente multi-thread, quando più thread accedono o modificano gli stessi dati contemporaneamente, il risultato finale è il tempo di esecuzione del thread.

  • Se non è presente un meccanismo di sincronizzazione, si verificheranno condizioni di competizione che potrebbero portare a dati imprecisi o eccezioni al programma.

  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. }

Sezione critica

  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) viene utilizzato per impedire a più thread di accedere o modificare contemporaneamente le risorse condivise.

  • Solo un thread alla volta può possedere il mutex. Se un thread assume la proprietà del mutex, gli altri thread che richiedono il mutex verranno bloccati fino al rilascio dell'autorizzazione mutex.

  • Crea un mutex - CreateMutex

  • Richiedi mutex - WaitForSingleObject

  • Rilascia il 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. }

Sezione critica e mutex

  • sezione critica

    • Meccanismo di sincronizzazione dei thread per risorse condivise, le sezioni critiche forniscono accesso mutuamente esclusivo tra thread dello stesso processo.

    • Ogni thread deve essere in grado di accedere alla sezione critica prima di accedere alle risorse condivise e lasciare la sezione critica una volta completato l'accesso per completare la sincronizzazione dei thread.

  • mutex

    • Il meccanismo di sincronizzazione dei thread viene utilizzato per impedire a più thread di accedere contemporaneamente alle risorse condivise.

    • I mutex possono sincronizzare processi o thread e possono essere sincronizzati tra processi.
      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. }
  • prestazione

    • Le sezioni critiche sono più veloci dei mutex all'interno dei thread dello stesso processo.

  • Funzione

    • I mutex possono essere sincronizzati tra processi, ma le sezioni critiche possono essere sincronizzate solo tra thread nello stesso processo.

  • proprietà

    • I mutex hanno severi requisiti di proprietà e solo i thread con autorizzazioni mutex possono rilasciarlo.

  • situazione di stallo
    • Quando un thread muore inaspettatamente mentre si mantiene un blocco (eccezione)
    • Se un thread termina in modo imprevisto mentre mantiene un blocco della sezione critica, il blocco non verrà rilasciato, impedendo agli altri thread in attesa che la sezione critica sia in grado di essere eseguito normalmente, determinando un deadlock.
      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. }
    • Se un thread termina in modo imprevisto mentre mantiene un blocco mutex, Windows ne rilascia automaticamente la proprietà in modo che gli altri thread possano continuare a essere eseguiti 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. }

Semaforo

  • Un semaforo è un oggetto di sincronizzazione utilizzato per controllare l'accesso alle risorse condivise da più thread. È un contatore che rappresenta la quantità di risorse disponibili. Quando il valore del semaforo è maggiore di 0 indica che le risorse sono disponibili; quando il valore è 0 indica che non ci sono risorse disponibili;

    • Aspettare : Tenta di diminuire il valore del semaforo. Se il valore del semaforo è maggiore di 0, decrementalo di 1 e continua l'esecuzione. Se il valore del semaforo è 0, il thread si blocca finché il valore del semaforo non diventa maggiore di 0.

    • liberato : Aumenta il valore del semaforo. Se ci sono altri thread bloccati in attesa di questo semaforo, uno di essi verrà risvegliato.

  • Crea semaforo

    • Sui sistemi Windows, utilizzare CreateSemaphore OCreateSemaphoreEx La funzione crea un semaforo.

  • Attendi (Wait) e rilascia (Release) il semaforo

    • L'attesa al semaforo viene solitamente eseguita utilizzando WaitForSingleObject OWaitForMultipleObjects funzione.

    • Rilascia l'utilizzo del semaforo ReleaseSemaphore funzione.

  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

  • Nella programmazione Windows, gli eventi sono un meccanismo di sincronizzazione utilizzato per inviare segnali tra più thread.L'oggetto evento può essereripristino manualeOripristino automatico

    • Evento Reset Manuale: Quando un evento viene impostato (segnalato), rimarrà in questo stato fino al reset esplicito. Ciò significa che più thread in attesa dell'evento possono essere risvegliati prima che l'evento venga reimpostato.

    • Evento di ripristino automatico: quando un evento viene ricevuto (segnalato) da un thread in attesa, il sistema reimposta automaticamente lo stato dell'evento su non segnalato (non segnalato). Ciò significa che è consentito risvegliare un solo thread alla volta.

  • Crea Evento

    • Utilizzo delle funzioni API di WindowsCreateEventÈ possibile creare un oggetto evento

    • lpEventAttributes: puntatore all'attributo di sicurezza, se impostato suNULL, viene utilizzata la sicurezza predefinita.

    • bManualReset: SeTRUE, viene creato un evento di ripristino manuale, altrimenti viene creato un evento di ripristino automatico.

    • bInitialState: SeTRUE, allora lo stato iniziale è lo stato del segnale seFALSE, è uno stato di non segnalazione.

    • lpName: il nome dell'evento.

  • Per impostare un evento (impostare lo stato dell'evento sullo stato del segnale) utilizzareSetEventfunzione

  • Per ripristinare un evento (impostare lo stato dell'evento su uno stato non segnalato) utilizzareResetEventfunzione

  • Attendi che l'evento attenda che un oggetto evento diventi uno stato di segnale.WaitForSingleObjectfunzione

  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. }