Teknologian jakaminen

WIN32-ydinohjelmointi – säikeen käyttö (2) Synkroninen keskinäinen poissulkeminen

2024-07-12

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

Sisällysluettelo

kilpailun kunto

Kriittinen osasto

Mutex

CriticalSection & Mutex

Semafori

Tapahtuma


kilpailun kunto

  • Monisäikeisessä ympäristössä, kun useat säikeet käyttävät tai muokkaavat samaa dataa samanaikaisesti, lopputulos on säikeen suoritusaika.

  • Jos synkronointimekanismia ei ole, esiintyy kilpailuolosuhteita, jotka voivat johtaa epätarkkoihin tietoihin tai ohjelmapoikkeuksiin.

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

Kriittinen osasto

  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

  • Mutexia (Mutex) käytetään estämään useita säikeitä pääsemästä tai muokkaamaan jaettuja resursseja samanaikaisesti.

  • Vain yksi säie voi omistaa mutexin samanaikaisesti. Jos yksi säie omistaa mutexin, muut mutexia pyytävät säikeet estetään, kunnes mutex-lupa vapautetaan.

  • Luo mutex - CreateMutex

  • Pyydä mutexia - WaitForSingleObject

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

CriticalSection & Mutex

  • kriittinen jakso

    • Jaettujen resurssien säikeiden synkronointimekanismi, kriittiset osat tarjoavat toisensa poissulkevan pääsyn saman prosessin säikeiden välillä.

    • Jokaisen säikeen on kyettävä siirtymään kriittiseen osioon ennen jaettujen resurssien käyttöä ja poistumaan kriittisestä osiosta käyttöoikeuden päätyttyä, jotta säikeen synkronointi on valmis.

  • mutex

    • Säikeiden synkronointimekanismia käytetään rajoittamaan useita säikeitä pääsemästä jaettuihin resursseihin samanaikaisesti.

    • Mutexet voivat synkronoida prosesseja tai säikeitä, ja ne voidaan synkronoida prosessien välillä.
      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. }
  • esitys

    • Kriittiset osat ovat nopeampia kuin mutexit saman prosessin säikeissä.

  • Toiminto

    • Mutexes voidaan synkronoida prosessien välillä, mutta kriittiset osat voidaan synkronoida vain säikeiden välillä samassa prosessissa.

  • omistus

    • Mutexeilla on tiukat omistusvaatimukset, ja vain säikeet, joilla on mutex-oikeudet, voivat vapauttaa sen.

  • umpikuja
    • Kun lanka kuolee odottamatta lukkoa pitäessäsi (poikkeus)
    • Jos säie päättyy odottamatta kriittisen osion lukituksen ollessa päällä, lukkoa ei vapauteta, jolloin muut kriittistä osaa odottavat säikeet eivät pysty suorittamaan normaalisti, mikä johtaa lukkiutumiseen.
      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. }
    • Jos säie päättyy odottamatta mutex-lukon ollessa päällä, Windows vapauttaa automaattisesti omistajuutensa, jotta muut säikeet voivat jatkaa toimintaansa normaalisti.
      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. }

Semafori

  • Semafori on synkronointiobjekti, jota käytetään hallitsemaan useiden säikeiden pääsyä jaettuihin resursseihin. Se on laskuri, joka edustaa käytettävissä olevien resurssien määrää. Kun semaforin arvo on suurempi kuin 0, se osoittaa, että resurssit ovat käytettävissä, kun arvo on 0, se osoittaa, että resursseja ei ole käytettävissä.

    • odota : Yrittää pienentää semaforin arvoa. Jos semaforin arvo on suurempi kuin 0, vähennä sitä yhdellä ja jatka suorittamista. Jos semaforin arvo on 0, lanka blokkaa, kunnes semaforin arvo on suurempi kuin 0.

    • vapautettu : Lisää semaforin arvoa. Jos muita lankoja on estetty odottamassa tätä semaforia, yksi niistä herää.

  • Luo semafori

    • Windows-järjestelmissä käytä CreateSemaphore taiCreateSemaphoreEx Funktio luo semaforin.

  • Odota (Odota) ja vapauta (Vapauta) semafori

    • Semaforin odottaminen tapahtuu yleensä käyttämällä WaitForSingleObject taiWaitForMultipleObjects toiminto.

    • Vapauta semaforien käyttö ReleaseSemaphore toiminto.

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

Tapahtuma

  • Windows-ohjelmoinnissa tapahtumat ovat synkronointimekanismi, jota käytetään signaalien lähettämiseen useiden säikeiden välillä.Tapahtumaobjekti voi ollamanuaalinen nollaustaiautomaattinen nollaus

    • Manuaalinen nollaustapahtuma: Kun tapahtuma on asetettu (signaloitu), se pysyy tässä tilassa, kunnes se nollataan erikseen. Tämä tarkoittaa, että useita tapahtumaa odottavia säikeitä voidaan herättää ennen kuin tapahtuma nollataan.

    • Automaattinen nollaustapahtuma: Kun odottava säie vastaanottaa (signaloitu) tapahtuman, järjestelmä palauttaa automaattisesti tapahtuman tilan signaloimattomaan (signaloitumattomaan). Tämä tarkoittaa, että vain yksi lanka saa herättää kerrallaan.

  • Luo tapahtuma

    • Windows API -toimintojen käyttäminenCreateEventTapahtumaobjekti voidaan luoda

    • lpEventAttributes: Osoitin suojausattribuutille, jos se on asetettuNULL, käytetään oletussuojausta.

    • bManualReset: josTRUE, sitten luodaan manuaalinen nollaustapahtuma, muuten luodaan automaattinen nollaustapahtuma.

    • bInitialState: josTRUE, niin alkutila on signaalin tila;FALSE, se on ei-signaloiva tila.

    • lpName: Tapahtuman nimi.

  • Aseta tapahtuma (asettaa tapahtuman tila signaalitilaan) käyttämälläSetEventtoiminto

  • Voit nollata tapahtuman (asettaa tapahtuman tilaksi ei-signaloidun tilan) käyttämälläResetEventtoiminto

  • Odota, että tapahtuma odottaa, että tapahtumaobjektista tulee signaalitila.WaitForSingleObjecttoiminto

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