1. 程式人生 > >windows編程 使用C++實現多線程類

windows編程 使用C++實現多線程類

size ring oid rime 業務 res rim .net 通知

有時候我們想在一個類中實現多線程,主線程在某些時刻獲得數據,可以“通知”子線程去處理,然後把結果返回。下面的實例是主線程每隔2s產生10個隨機數,將這10隨機數傳給多線程類,讓它接收到數據後馬上打印出來。

首先看類的定義:

[cpp] view plain copy
  1. #pragma once
  2. #include <iostream>
  3. #include <atlbase.h> // 使用到了atl類
  4. #include <atlsync.h>
  5. #include <vector>
  6. using namespace std;
  7. class CMultiThreadTest
  8. {
  9. public:
  10. bool Init(); // 初始化類成員
  11. bool UnInit(); // 釋放資源
  12. void NotifyDowork(const std::vector<int> &data);
  13. static DWORD CALLBACK TestThread(LPVOID); // 線程函數,必須是靜態函數
  14. DWORD TestProc(); // 線程工作實現
  15. private:
  16. std::vector<int> m_data; // 同步數據
  17. ATL::CEvent m_NotifyEvent; // 通知事件
  18. HANDLE m_hThread; // 線程句柄
  19. };


類中使用到了ALT類,需要包含atlbase.h和altsync.h頭文件。函數TestThread必須是靜態函數,因為CreateThread只接受全局或者靜態函數。
首先先看Init()和UnInit()函數的實現:

[cpp] view plain copy
  1. bool CMultiThreadTest::Init()
  2. {
  3. // 創建事件
  4. BOOL bRet = m_NotifyEvent.Create(NULL, TRUE, FALSE, NULL);
  5. if (!bRet) {
  6. return false;
  7. }
  8. // 掛起的方式創建線程
  9. m_hThread = CreateThread(NULL, 0, &CMultiThreadTest::TestThread, this, CREATE_SUSPENDED, NULL);
  10. if (NULL == m_hThread) {
  11. return false;
  12. }
  13. // 喚醒線程
  14. ResumeThread(m_hThread);
  15. return true;
  16. }
  17. bool CMultiThreadTest::UnInit()
  18. {
  19. // 通知線程處理data的數據
  20. if (m_NotifyEvent != NULL) {
  21. m_NotifyEvent.Set();
  22. }
  23. if (m_hThread != NULL)
  24. {
  25. // 預留100ms讓線程處理完數據,100ms是個估值
  26. WaitForSingleObject(m_hThread, 100);
  27. CloseHandle(m_hThread);
  28. m_hThread = NULL;
  29. }
  30. return true;
  31. }


ATL::CEvent的成員函數Create接收4個參數,第四個參數指定Event的名字(它是可以有名字的),以便在其他進程可以找到該事件,這裏我們不需要使用,把它設置為NULL,其他參數很容易理解,不贅述。
Init()函數值得註意的是我們創建的線程是以掛起的方式創建,所以必須調用ResumeThread喚醒線程,否則線程一值處於沈睡狀態,不執行線程函數。
UnInit()函數比較簡單,主要通知線程執行收尾工作,並釋放類的資源。

下面我們來看看剩下的函數的實現。

[cpp] view plain copy
  1. DWORD CALLBACK CMultiThreadTest::TestThread(LPVOID lpParam)
  2. {
  3. if (lpParam == NULL) {
  4. return 0;
  5. }
  6. CMultiThreadTest *lpThis = reinterpret_cast<CMultiThreadTest *>(lpParam);
  7. return lpThis->TestProc();
  8. }
  9. DWORD CMultiThreadTest::TestProc()
  10. {
  11. while (true)
  12. {
  13. // 每5s監聽一次,秒數直接影響程序的性能
  14. DWORD dwRet = WaitForSingleObject(m_NotifyEvent, 5000);
  15. // 進入循環5s沒有事件發生,不做任何處理
  16. if (dwRet == WAIT_TIMEOUT) {
  17. continue;
  18. }
  19. // 打印數組
  20. for (unsigned int i = 0; i < m_data.size(); i++)
  21. {
  22. cout <<m_data[i] <<" ";
  23. }
  24. cout <<endl;
  25. // 重置事件
  26. m_NotifyEvent.Reset();
  27. }
  28. return 0;
  29. }
  30. void CMultiThreadTest::NotifyDowork(const std::vector<int> &data)
  31. {
  32. m_data = data;
  33. m_NotifyEvent.Set(); // 通知線程該做事情了!
  34. }


首先我們看TestThread函數,它是線程的“入口“,線程被喚醒後執行該函數。值得註意的是,我們在創建線程的時候把對象指針this作為參數傳遞給創建線程函數,系統在調用TestThread的時候會把this傳遞回來,這裏使用弱類型轉換reinterpret_cast將LPVOID轉化為CMultiThreadTest類的指針,reinterpret_cast是一個危險的類型轉換,一般只適用於指針和整數之間的轉換。有興趣的同學可以參考C++ Primer第4版18.2.1章節。


線程函數將參數lpParam轉化為對象指針後,執行對象的成員函數TestProc(),TestProc()實現主要的邏輯。這裏可能會有人疑問,為什麽不直接在TestThread()函數實現主要邏輯呢?這樣做有兩個好處,一是能夠將線程函數邏輯和業務邏輯分離,其二就是TestThread是個靜態函數,類靜態函數只能處理類的靜態成員變量,而很多時候我們希望線程處理類的非靜態成員變量。
最後NotifyDowork函數很簡單,該函數給外部調用,它把外部傳進來的data賦值給類的非靜態成員變量m_data,並通知線程處理m_data數據,TestProc中WaitForSingleObject函數接收到事件後往下執行,把m_data打印出來。

下面我們看看main函數的實現:

[cpp] view plain copy
  1. int _tmain(int argc, _TCHAR* argv[])
  2. {
  3. CMultiThreadTest multiThreadTest;
  4. // 初始化失敗
  5. if (!multiThreadTest.Init()) {
  6. return 0;
  7. }
  8. srand(unsigned int(time(NULL)));
  9. std::vector<int> data;
  10. while (true)
  11. {
  12. data.clear();
  13. // 產生10個隨機數
  14. for (int i = 0; i < 10; i++)
  15. data.push_back(rand() % 1000);
  16. // 通知多線程類執行工作
  17. multiThreadTest.NotifyDowork(data);
  18. Sleep(2000);
  19. }
  20. multiThreadTest.UnInit();
  21. return 0;
  22. }


這段代碼就不用解釋了,記得包含頭文件windows.h、time.h和vector。

總結:
多線程類的使用場景是,當一個線程或得到數據後,希望其他線程能夠處理這部分數。多線程類實現還是比較簡單的,首先創建線程和線程事件,實現給外部調用的接口,外部通過接口設置事件,通知線程執行。

windows編程 使用C++實現多線程類