Κοινή χρήση τεχνολογίας

WIN32 Core Programming - Thread Operation (2) Synchronous Mutual Exclusion

2024-07-12

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

Πίνακας περιεχομένων

κατάσταση του αγώνα

Κρίσιμο Τμήμα

Mutex

CriticalSection & Mutex

Σηματοφόρος

Εκδήλωση


κατάσταση του αγώνα

  • Σε ένα περιβάλλον πολλαπλών νημάτων, όταν πολλά νήματα έχουν πρόσβαση ή τροποποιούν τα ίδια δεδομένα ταυτόχρονα, το τελικό αποτέλεσμα είναι ο χρόνος εκτέλεσης του νήματος.

  • Εάν δεν υπάρχει μηχανισμός συγχρονισμού, θα προκύψουν συνθήκες αγώνα, κάτι που μπορεί να οδηγήσει σε ανακριβή δεδομένα ή εξαιρέσεις προγραμμάτων.

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

Κρίσιμο Τμήμα

  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) χρησιμοποιείται για την αποτροπή πολλαπλών νημάτων από την πρόσβαση ή την τροποποίηση κοινόχρηστων πόρων ταυτόχρονα.

  • Μόνο ένα νήμα μπορεί να είναι κάτοχος του mutex ταυτόχρονα.

  • Δημιουργία mutex - CreateMutex

  • Αίτημα mutex - WaitForSingleObject

  • Αφήστε το 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

  • κρίσιμο τμήμα

    • Μηχανισμός συγχρονισμού νημάτων για κοινόχρηστους πόρους, κρίσιμες ενότητες παρέχουν αμοιβαία αποκλειστική πρόσβαση μεταξύ νημάτων της ίδιας διαδικασίας.

    • Κάθε νήμα πρέπει να μπορεί να εισέλθει στην κρίσιμη ενότητα πριν αποκτήσει πρόσβαση σε κοινόχρηστους πόρους και να αποχωρήσει από την κρίσιμη ενότητα μετά την ολοκλήρωση της πρόσβασης για να ολοκληρωθεί ο συγχρονισμός νημάτων.

  • mutex

    • Ο μηχανισμός συγχρονισμού νημάτων χρησιμοποιείται για τον περιορισμό της ταυτόχρονης πρόσβασης πολλαπλών νημάτων σε κοινόχρηστους πόρους.

    • Τα Mutexe μπορούν να συγχρονίσουν διεργασίες ή νήματα και μπορούν να συγχρονιστούν μεταξύ των διεργασιών.
      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. }
  • εκτέλεση

    • Τα κρίσιμα τμήματα είναι ταχύτερα από τα mutexe μέσα σε νήματα της ίδιας διαδικασίας.

  • Λειτουργία

    • Τα Mutexes μπορούν να συγχρονιστούν σε όλες τις διεργασίες, αλλά τα κρίσιμα τμήματα μπορούν να συγχρονιστούν μόνο μεταξύ νημάτων στην ίδια διεργασία.

  • ιδιοκτησία

    • Τα Mutexes έχουν αυστηρές απαιτήσεις ιδιοκτησίας και μόνο τα νήματα με δικαιώματα mutex μπορούν να τα απελευθερώσουν.

  • αδιέξοδο
    • Όταν ένα νήμα πεθαίνει απροσδόκητα κρατώντας μια κλειδαριά (εξαίρεση)
    • Εάν ένα νήμα τερματιστεί απροσδόκητα ενώ κρατάτε ένα κλείδωμα κρίσιμου τμήματος, το κλείδωμα δεν θα απελευθερωθεί, με αποτέλεσμα άλλα νήματα που περιμένουν το κρίσιμο τμήμα να μην μπορούν να εκτελεστούν κανονικά, με αποτέλεσμα αδιέξοδο.
      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. }
    • Εάν ένα νήμα τερματιστεί απροσδόκητα ενώ κρατάτε ένα κλείδωμα mutex, τα Windows απελευθερώνουν αυτόματα την ιδιοκτησία του, έτσι ώστε τα άλλα νήματα να μπορούν να συνεχίσουν να εκτελούνται κανονικά.
      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. }

Σηματοφόρος

  • Ο σηματοφόρος είναι ένα αντικείμενο συγχρονισμού που χρησιμοποιείται για τον έλεγχο της πρόσβασης σε κοινόχρηστους πόρους από πολλαπλά νήματα. Είναι ένας μετρητής που αντιπροσωπεύει την ποσότητα των διαθέσιμων πόρων. Όταν η τιμή του σηματοφόρου είναι μεγαλύτερη από 0, σημαίνει ότι οι πόροι είναι διαθέσιμοι όταν η τιμή είναι 0, σημαίνει ότι δεν υπάρχουν διαθέσιμοι πόροι.

    • Περίμενε : Προσπάθειες μείωσης της τιμής του σηματοφόρου. Εάν η τιμή του σηματοφόρου είναι μεγαλύτερη από 0, μειώστε την κατά 1 και συνεχίστε την εκτέλεση. Εάν η τιμή του σηματοφόρου είναι 0, το νήμα μπλοκάρει έως ότου η τιμή του σηματοφόρου γίνει μεγαλύτερη από 0.

    • απελευθερώθηκε : Αυξήστε την αξία του σηματοφόρου. Εάν υπάρχουν μπλοκαρισμένα άλλα νήματα που περιμένουν αυτόν τον σηματοφόρο, ένα από αυτά θα αφυπνιστεί.

  • Δημιουργία σηματοφόρου

    • Σε συστήματα Windows, χρησιμοποιήστε CreateSemaphore ήCreateSemaphoreEx Η συνάρτηση δημιουργεί έναν σηματοφόρο.

  • Περιμένετε (Περιμένετε) και απελευθερώστε το σηματοφόρο (Απελευθέρωση).

    • Η αναμονή σε έναν σηματοφόρο συνήθως γίνεται χρησιμοποιώντας WaitForSingleObject ήWaitForMultipleObjects λειτουργία.

    • Απελευθερώστε τη χρήση σηματοφόρου ReleaseSemaphore λειτουργία.

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

Εκδήλωση

  • Στον προγραμματισμό των Windows, τα συμβάντα είναι ένας μηχανισμός συγχρονισμού που χρησιμοποιείται για την αποστολή σημάτων μεταξύ πολλαπλών νημάτων.Το αντικείμενο συμβάντος μπορεί να είναιμη αυτόματη επαναφοράήαυτόματη επαναφορά

    • Συμβάν χειροκίνητης επαναφοράς: Όταν ένα συμβάν έχει οριστεί (σηματοδοτηθεί), θα παραμείνει σε αυτήν την κατάσταση έως ότου γίνει ρητή επαναφορά. Αυτό σημαίνει ότι πολλά νήματα που περιμένουν το συμβάν μπορούν να αφυπνιστούν πριν από την επαναφορά του συμβάντος.

    • Αυτόματη επαναφορά συμβάντος: Όταν ένα συμβάν λαμβάνεται (σήμαινε) από ένα νήμα αναμονής, το σύστημα επαναφέρει αυτόματα την κατάσταση συμβάντος σε μη σηματοδοτημένο (μη σηματοδοτημένο). Αυτό σημαίνει ότι επιτρέπεται να αφυπνίζεται μόνο ένα νήμα τη φορά.

  • Δημιουργία εκδήλωσης

    • Χρήση λειτουργιών API των WindowsCreateEventΜπορεί να δημιουργηθεί ένα αντικείμενο συμβάντος

    • lpEventAttributes: Δείκτης στο χαρακτηριστικό ασφαλείας, εάν έχει οριστεί σεNULL, χρησιμοποιείται η προεπιλεγμένη ασφάλεια.

    • bManualReset: ανTRUE, τότε δημιουργείται ένα συμβάν μη αυτόματης επαναφοράς, διαφορετικά δημιουργείται ένα συμβάν αυτόματης επαναφοράς.

    • bInitialState: ανTRUE, τότε η αρχική κατάσταση είναι η κατάσταση σήματος εάνFALSE, είναι μια κατάσταση μη σηματοδότησης.

    • lpName: Το όνομα της εκδήλωσης.

  • Για να ορίσετε ένα συμβάν (ορισμός κατάστασης συμβάντος σε κατάσταση σήματος) χρησιμοποιήστεSetEventλειτουργία

  • Για να επαναφέρετε ένα συμβάν (ρυθμίστε την κατάσταση συμβάντος σε κατάσταση χωρίς σήμα) χρησιμοποιήστεResetEventλειτουργία

  • Περιμένετε μέχρι το συμβάν να περιμένει για ένα αντικείμενο συμβάντος να γίνει κατάσταση σήματος.WaitForSingleObjectλειτουργία

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