Berbagi teknologi

Pemrograman Inti WIN32 - Operasi Thread (2) Pengecualian Saling Sinkron

2024-07-12

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

Daftar isi

kondisi balapan

Bagian penting

Muteks

BagianKritis & Mutex

Tiang sinyal

Peristiwa


kondisi balapan

  • Dalam lingkungan multi-thread, ketika beberapa thread mengakses atau mengubah data yang sama secara bersamaan, hasil akhirnya adalah waktu eksekusi thread.

  • Jika tidak ada mekanisme sinkronisasi, kondisi balapan akan terjadi, yang dapat menyebabkan pengecualian data atau program tidak akurat.

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

Bagian penting

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

Muteks

  • Mutex (Mutex) digunakan untuk mencegah beberapa thread mengakses atau memodifikasi sumber daya bersama secara bersamaan.

  • Hanya satu thread yang dapat memiliki mutex pada saat yang sama. Jika satu thread mengambil alih kepemilikan mutex, thread lain yang meminta mutex akan diblokir hingga izin mutex dilepaskan.

  • Buat mutex - BuatMutex

  • Minta mutex - WaitForSingleObject

  • Lepaskan mutex - RilisMutex

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

BagianKritis & Mutex

  • bagian penting

    • Mekanisme sinkronisasi thread untuk sumber daya bersama, bagian penting menyediakan akses yang saling eksklusif antara thread dari proses yang sama.

    • Setiap thread harus dapat memasuki bagian penting sebelum mengakses sumber daya bersama, dan keluar dari bagian penting setelah akses selesai untuk menyelesaikan sinkronisasi thread.

  • mutex

    • Mekanisme sinkronisasi thread digunakan untuk membatasi beberapa thread mengakses sumber daya bersama secara bersamaan.

    • Mutex dapat menyinkronkan proses atau thread, dan dapat disinkronkan antar proses.
      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. }
  • pertunjukan

    • Bagian kritis lebih cepat daripada mutex dalam thread dari proses yang sama.

  • Fungsi

    • Mutex dapat disinkronkan antar proses, tetapi bagian penting hanya dapat disinkronkan antar thread dalam proses yang sama.

  • kepemilikan

    • Mutex memiliki persyaratan kepemilikan yang ketat, dan hanya thread dengan izin mutex yang dapat melepaskannya.

  • jalan buntu
    • Ketika thread tiba-tiba mati saat memegang kunci (pengecualian)
    • Jika sebuah thread berakhir secara tiba-tiba saat menahan kunci bagian kritis, kunci tersebut tidak akan dilepaskan, menyebabkan thread lain yang menunggu bagian kritis tidak dapat dijalankan secara normal, sehingga mengakibatkan kebuntuan.
      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. }
    • Jika sebuah thread tiba-tiba berhenti saat memegang kunci mutex, Windows secara otomatis melepaskan kepemilikannya sehingga thread lain dapat terus berjalan secara normal.
      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. }

Tiang sinyal

  • Semaphore adalah objek sinkronisasi yang digunakan untuk mengontrol akses ke sumber daya bersama oleh beberapa thread. Ini adalah penghitung yang mewakili jumlah sumber daya yang tersedia. Jika nilai semaphore lebih besar dari 0, berarti sumber daya tersedia; jika nilainya 0, berarti tidak ada sumber daya yang tersedia.

    • Tunggu : Upaya untuk menurunkan nilai semaphore. Jika nilai semaphore lebih besar dari 0, kurangi sebesar 1 dan lanjutkan eksekusi. Jika nilai semaphore adalah 0, thread akan diblokir hingga nilai semaphore menjadi lebih besar dari 0.

    • dibebaskan : Meningkatkan nilai semaphore. Jika ada thread lain yang diblokir menunggu semaphore ini, salah satunya akan dibangunkan.

  • Buat semafor

    • Pada sistem Windows, gunakan CreateSemaphore atauCreateSemaphoreEx Fungsi membuat semaphore.

  • Tunggu (Tunggu) dan lepaskan (Lepaskan) semaphore

    • Menunggu semaphore biasanya dilakukan dengan menggunakan WaitForSingleObject atauWaitForMultipleObjects fungsi.

    • Lepaskan penggunaan semaphore ReleaseSemaphore fungsi.

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

Peristiwa

  • Dalam pemrograman Windows, event adalah mekanisme sinkronisasi yang digunakan untuk mengirim sinyal antara beberapa thread.Objek acaranya bisa berupapengaturan ulang manualataupengaturan ulang otomatis

    • Peristiwa Penyetelan Ulang Manual: Ketika suatu peristiwa disetel (diberi sinyal), peristiwa tersebut akan tetap dalam keadaan ini hingga direset secara eksplisit. Ini berarti beberapa thread yang menunggu acara dapat dibangunkan sebelum acara direset.

    • Auto Reset Event: Ketika suatu event diterima (ditandai) oleh thread yang menunggu, sistem secara otomatis mereset status event menjadi non-signaled (tidak diberi sinyal). Artinya hanya satu thread yang diperbolehkan untuk dibangunkan dalam satu waktu.

  • Membuat acara

    • Menggunakan fungsi Windows APICreateEventObjek acara dapat dibuat

    • lpEventAttributes: Penunjuk ke atribut keamanan, jika disetel keNULL, keamanan default digunakan.

    • bManualReset: jikaTRUE, maka peristiwa penyetelan ulang manual akan dibuat, jika tidak, peristiwa penyetelan ulang otomatis akan dibuat.

    • bInitialState: jikaTRUE, maka keadaan awal adalah keadaan sinyal;FALSE, ini adalah keadaan tanpa sinyal.

    • lpName: Nama acara.

  • Untuk menyetel suatu peristiwa (atur status peristiwa ke status sinyal) gunakanSetEventfungsi

  • Untuk mengatur ulang suatu peristiwa (mengatur keadaan peristiwa ke keadaan non-sinyal) gunakanResetEventfungsi

  • Tunggu event menunggu objek event menjadi status sinyal.WaitForSingleObjectfungsi

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