標籤:csdn win plain 弱類型 lan 類型 設定 mil windows編程
有時候我們想在一個類中實現多線程,主線程在某些時刻獲得資料,可以“通知”子線程去處理,然後把結果返回。下面的執行個體是主線程每隔2s產生10個隨機數,將這10隨機數傳給多線程類,讓它接收到資料後馬上列印出來。
首先看類的定義:
[cpp] view plain copy
- #pragma once
- #include <iostream>
- #include <atlbase.h> // 使用到了atl類
- #include <atlsync.h>
- #include <vector>
- using namespace std;
-
- class CMultiThreadTest
- {
- public:
- bool Init(); // 初始化類成員
- bool UnInit(); // 釋放資源
- void NotifyDowork(const std::vector<int> &data);
-
- static DWORD CALLBACK TestThread(LPVOID); // 線程函數,必須是靜態函數
- DWORD TestProc(); // 線程工作實現
-
- private:
- std::vector<int> m_data; // 同步資料
- ATL::CEvent m_NotifyEvent; // 通知事件
- HANDLE m_hThread; // 線程控制代碼
- };
類中使用到了ALT類,需要包含atlbase.h和altsync.h標頭檔。函數TestThread必須是靜態函數,因為CreateThread只接受全域或者靜態函數。
首先先看Init()和UnInit()函數的實現:
[cpp] view plain copy
- bool CMultiThreadTest::Init()
- {
- // 建立事件
- BOOL bRet = m_NotifyEvent.Create(NULL, TRUE, FALSE, NULL);
- if (!bRet) {
- return false;
- }
-
- // 掛起的方式建立線程
- m_hThread = CreateThread(NULL, 0, &CMultiThreadTest::TestThread, this, CREATE_SUSPENDED, NULL);
- if (NULL == m_hThread) {
- return false;
- }
-
- // 喚醒線程
- ResumeThread(m_hThread);
- return true;
- }
-
-
- bool CMultiThreadTest::UnInit()
- {
- // 通知線程處理data的資料
- if (m_NotifyEvent != NULL) {
- m_NotifyEvent.Set();
- }
-
- if (m_hThread != NULL)
- {
- // 預留100ms讓線程處理完資料,100ms是個估值
- WaitForSingleObject(m_hThread, 100);
- CloseHandle(m_hThread);
- m_hThread = NULL;
- }
-
- return true;
- }
ATL::CEvent的成員函數Create接收4個參數,第四個參數指定Event的名字(它是可以有名字的),以便在其他進程可以找到該事件,這裡我們不需要使用,把它設定為NULL,其他參數很容易理解,不贅述。
Init()函數值得注意的是我們建立的線程是以掛起的方式建立,所以必須調用ResumeThread喚醒線程,否則線程一值處於沉睡狀態,不執行線程函數。
UnInit()函數比較簡單,主要通知線程執行收尾工作,並釋放類的資源。
下面我們來看看剩下的函數的實現。
[cpp] view plain copy
- DWORD CALLBACK CMultiThreadTest::TestThread(LPVOID lpParam)
- {
- if (lpParam == NULL) {
- return 0;
- }
-
- CMultiThreadTest *lpThis = reinterpret_cast<CMultiThreadTest *>(lpParam);
- return lpThis->TestProc();
- }
-
-
- DWORD CMultiThreadTest::TestProc()
- {
- while (true)
- {
- // 每5s監聽一次,秒數直接影響程式的效能
- DWORD dwRet = WaitForSingleObject(m_NotifyEvent, 5000);
-
- // 進入迴圈5s沒有事件發生,不做任何處理
- if (dwRet == WAIT_TIMEOUT) {
- continue;
- }
-
- // 列印數組
- for (unsigned int i = 0; i < m_data.size(); i++)
- {
- cout <<m_data[i] <<" ";
- }
-
- cout <<endl;
-
- // 重設事件
- m_NotifyEvent.Reset();
- }
- return 0;
- }
-
-
- void CMultiThreadTest::NotifyDowork(const std::vector<int> &data)
- {
- m_data = data;
- m_NotifyEvent.Set(); // 通知線程該做事情了!
- }
首先我們看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
- int _tmain(int argc, _TCHAR* argv[])
- {
- CMultiThreadTest multiThreadTest;
-
- // 初始化失敗
- if (!multiThreadTest.Init()) {
- return 0;
- }
-
- srand(unsigned int(time(NULL)));
- std::vector<int> data;
-
- while (true)
- {
- data.clear();
-
- // 產生10個隨機數
- for (int i = 0; i < 10; i++)
- data.push_back(rand() % 1000);
-
- // 通知多線程類執行工作
- multiThreadTest.NotifyDowork(data);
-
- Sleep(2000);
- }
-
- multiThreadTest.UnInit();
- return 0;
- }
這段代碼就不用解釋了,記得包含標頭檔windows.h、time.h和vector。
總結:
多線程類的使用情境是,當一個線程或得到資料後,希望其他線程能夠處理這部分數。多線程類實現還是比較簡單的,首先建立線程和線程事件,實現給外部調用的介面,外部通過介面設定事件,通知線程執行。
windows編程 使用C++實現多線程類