<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                > 原文:http://blog.csdn.net/luoweifu/article/details/46835437 > 作者:[luoweifu](http://blog.csdn.net/luoweifu)? 《[編程思想之多線程與多進程(1)——以操作系統的角度述說線程與進程](72721)》一文詳細講述了線程、進程的關系及在操作系統中的表現,《[編程思想之多線程與多進程(2)——線程優先級與線程安全](72722)》一文講了線程安全(各種同步鎖)和優先級,這是多線程學習必須了解的基礎。本文將接著講一下C++中多線程程序的開發.這里主要講Windows平臺線程的用法,創建線程要調用windows API的CreateThread方法。 [TOC] # 創建線程 在Windows平臺,Windows API提供了對多線程的支持。前面進程和線程的概念中我們提到,一個程序至少有一個線程,這個線程稱為主線程(main thread),如果我們不顯示地創建線程,那我們產的程序就是只有主線程的間線程程序。? 下面,我們看看Windows中線程相關的操作和方法: ## CreateThread與CloseHandle CreateThread用于創建一個線程,其函數原型如下: ~~~ HANDLE WINAPI CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, //線程安全相關的屬性,常置為NULL SIZE_T dwStackSize, //新線程的初始化棧在大小,可設置為0 LPTHREAD_START_ROUTINE lpStartAddress, //被線程執行的回調函數,也稱為線程函數 LPVOID lpParameter, //傳入線程函數的參數,不需傳遞參數時為NULL DWORD dwCreationFlags, //控制線程創建的標志 LPDWORD lpThreadId //傳出參數,用于獲得線程ID,如果為NULL則不返回線程ID ); ~~~ **說明:**lpThreadAttributes:指向SECURITY_ATTRIBUTES結構的指針,決定返回的句柄是否可被子進程繼承,如果為NULL則表示返回的句柄不能被子進程繼承。 dwStackSize :線程棧的初始化大小,字節單位。系統分配這個值對 lpStartAddress:指向一個函數指針,該函數將被線程調用執行。因此該函數也被稱為線程函數(ThreadProc),是線程執行的起始地址,線程函數是一個回調函數,由操作系統在線程中調用。線程函數的原型如下: ~~~ DWORD WINAPI ThreadProc(LPVOID lpParameter); //lpParameter是傳入的參數,是一個空指針 ~~~ lpParameter:傳入線程函數(ThreadProc)的參數,不需傳遞參數時為NULL dwCreationFlags:控制線程創建的標志,有三個類型,0:線程創建后立即執行線程;CREATE_SUSPENDED:線程創建后進入就緒狀態,直到線程被喚醒時才調用;STACK_SIZE_PARAM_IS_A_RESERVATION:dwStackSize 參數指定線程初始化棧的大小,如果STACK_SIZE_PARAM_IS_A_RESERVATION標志未指定,dwStackSize將會設為系統預留的值。 返回值:如果線程創建成功,則返回這個新線程的句柄,否則返回NULL。如果線程創建失敗,可通過GetLastError函數獲得錯誤信息。 ~~~ BOOL WINAPI CloseHandle(HANDLE hObject); //關閉一個被打開的對象句柄 ~~~ 可用這個函數關閉創建的線程句柄,如果函數執行成功則返回true(非0),如果失敗則返回false(0),如果執行失敗可調用GetLastError.函數獲得錯誤信息。 ### 【Demo1】:創建一個最簡單的線程 ~~~ #include "stdafx.h" #include <windows.h> #include <iostream> using namespace std; //線程函數 DWORD WINAPI ThreadProc(LPVOID lpParameter) { for (int i = 0; i < 5; ++ i) { cout << "子線程:i = " << i << endl; Sleep(100); } return 0L; } int main() { //創建一個線程 HANDLE thread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL); //關閉線程 CloseHandle(thread); //主線程的執行路徑 for (int i = 0; i < 5; ++ i) { cout << "主線程:i = " << i << endl; Sleep(100); } return 0; } ~~~ > 結果如下:? > 主線程:i = 0? > 子線程:i = 0? > 主線程:i = 1? > 子線程:i = 1? > 子線程:i = 2? > 主線程:i = 2? > 子線程:i = 3? > 主線程:i = 3? > 子線程:i = 4? > 主線程:i = 4 ### 【Demo2】:在線程函數中傳入參數 ~~~ #include "stdafx.h" #include <windows.h> #include <iostream> using namespace std; #define NAME_LINE 40 //定義線程函數傳入參數的結構體 typedef struct __THREAD_DATA { int nMaxNum; char strThreadName[NAME_LINE]; __THREAD_DATA() : nMaxNum(0) { memset(strThreadName, 0, NAME_LINE * sizeof(char)); } }THREAD_DATA; //線程函數 DWORD WINAPI ThreadProc(LPVOID lpParameter) { THREAD_DATA* pThreadData = (THREAD_DATA*)lpParameter; for (int i = 0; i < pThreadData->nMaxNum; ++ i) { cout << pThreadData->strThreadName << " --- " << i << endl; Sleep(100); } return 0L; } int main() { //初始化線程數據 THREAD_DATA threadData1, threadData2; threadData1.nMaxNum = 5; strcpy(threadData1.strThreadName, "線程1"); threadData2.nMaxNum = 10; strcpy(threadData2.strThreadName, "線程2"); //創建第一個子線程 HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc, &threadData1, 0, NULL); //創建第二個子線程 HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc, &threadData2, 0, NULL); //關閉線程 CloseHandle(hThread1); CloseHandle(hThread2); //主線程的執行路徑 for (int i = 0; i < 5; ++ i) { cout << "主線程 === " << i << endl; Sleep(100); } system("pause"); return 0; } ~~~ 結果: > 主線程 === 線程1 — 0? > 0? > 線程2 — 0? > 線程1 — 1? > 主線程 === 1? > 線程2 — 1? > 主線程 === 2? > 線程1 — 2? > 線程2 — 2? > 主線程 === 3? > 線程2 — 3? > 線程1 — 3? > 主線程 === 4? > 線程2 — 4? > 線程1 — 4? > 線程2 — 5? > 請按任意鍵繼續… 線程2 — 6? > 線程2 — 7? > 線程2 — 8? > 線程2 — 9 > > * * * ## CreateMutex、WaitForSingleObject、ReleaseMutex 從【Demo2】中可以看出,雖然創建的子線程都正常執行起來了,但輸出的結果并不是我們預期的效果。我們預期的效果是每輸出一條語句后自動換行,但結果卻并非都是這樣。這是因為在線程執行時沒有做同步處理,比如第一行的輸出,主線程輸出“**主線程 ===**”后時間片已用完,這時輪到子線程1輸出,在子線程輸出“**線程1 —**”后時間片也用完了,這時又輪到主線程執行輸出“**0**”,之后又輪到子線程1輸出“**0**”。于是就出現了“**主線程 === 線程1 — 0 0**”的結果。 主線程:cout << “主線程 === ” << i << endl;? 子線程:cout strThreadName << ” — ” << i << endl; 為避免出現這種情況,我們對線程做一些簡單的同步處理,這里我們用互斥量(Mutex),關于互斥量(Mutex)的概念,請看《[編程思想之多線程與多進程(2)——線程優先級與線程安全](http://blog.csdn.net/luoweifu/article/details/46701167)》一文;更多C++線程同步的處理,請看下一節。 在使用互斥量進行線程同步時會用到以下幾個函數: ~~~ HANDLE WINAPI CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, //線程安全相關的屬性,常置為NULL BOOL bInitialOwner, //創建Mutex時的當前線程是否擁有Mutex的所有權 LPCTSTR lpName //Mutex的名稱 ); ~~~ **說明:**lpMutexAttributes也是表示安全的結構,與CreateThread中的lpThreadAttributes功能相同,表示決定返回的句柄是否可被子進程繼承,如果為NULL則表示返回的句柄不能被子進程繼承。bInitialOwner表示創建Mutex時的當前線程是否擁有Mutex的所有權,若為TRUE則指定為當前的創建線程為Mutex對象的所有者,其它線程訪問需要先ReleaseMutex。lpName為Mutex的名稱。 ~~~ DWORD WINAPI WaitForSingleObject( HANDLE hHandle, //要獲取的鎖的句柄 DWORD dwMilliseconds //超時間隔 ); ~~~ **說明:**WaitForSingleObject的作用是等待一個指定的對象(如Mutex對象),直到該對象處于非占用的狀態(如Mutex對象被釋放)或超出設定的時間間隔。除此之外,還有一個與它類似的函數WaitForMultipleObjects,它的作用是等待一個或所有指定的對象,直到所有的對象處于非占用的狀態,或超出設定的時間間隔。? hHandle:要等待的指定對象的句柄。dwMilliseconds:超時的間隔,以毫秒為單位;如果dwMilliseconds為非0,則等待直到dwMilliseconds時間間隔用完或對象變為非占用的狀態,如果dwMilliseconds 為INFINITE則表示無限等待,直到等待的對象處于非占用的狀態。 ~~~ BOOL WINAPI ReleaseMutex(HANDLE hMutex); ~~~ **說明:**釋放所擁有的互斥量鎖對象,hMutex為釋放的互斥量的句柄。 ### 【Demo3】:線程同步 ~~~ #include "stdafx.h" #include <windows.h> #include <iostream> #define NAME_LINE 40 //定義線程函數傳入參數的結構體 typedef struct __THREAD_DATA { int nMaxNum; char strThreadName[NAME_LINE]; __THREAD_DATA() : nMaxNum(0) { memset(strThreadName, 0, NAME_LINE * sizeof(char)); } }THREAD_DATA; HANDLE g_hMutex = NULL; //互斥量 //線程函數 DWORD WINAPI ThreadProc(LPVOID lpParameter) { THREAD_DATA* pThreadData = (THREAD_DATA*)lpParameter; for (int i = 0; i < pThreadData->nMaxNum; ++ i) { //請求獲得一個互斥量鎖 WaitForSingleObject(g_hMutex, INFINITE); cout << pThreadData->strThreadName << " --- " << i << endl; Sleep(100); //釋放互斥量鎖 ReleaseMutex(g_hMutex); } return 0L; } int main() { //創建一個互斥量 g_hMutex = CreateMutex(NULL, FALSE, NULL); //初始化線程數據 THREAD_DATA threadData1, threadData2; threadData1.nMaxNum = 5; strcpy(threadData1.strThreadName, "線程1"); threadData2.nMaxNum = 10; strcpy(threadData2.strThreadName, "線程2"); //創建第一個子線程 HANDLE hThread1 = CreateThread(NULL, 0, ThreadProc, &threadData1, 0, NULL); //創建第二個子線程 HANDLE hThread2 = CreateThread(NULL, 0, ThreadProc, &threadData2, 0, NULL); //關閉線程 CloseHandle(hThread1); CloseHandle(hThread2); //主線程的執行路徑 for (int i = 0; i < 5; ++ i) { //請求獲得一個互斥量鎖 WaitForSingleObject(g_hMutex, INFINITE); cout << "主線程 === " << i << endl; Sleep(100); //釋放互斥量鎖 ReleaseMutex(g_hMutex); } system("pause"); return 0; } ~~~ 結果: > 主線程 === 0? > 線程1 — 0? > 線程2 — 0? > 主線程 === 1? > 線程1 — 1? > 線程2 — 1? > 主線程 === 2? > 線程1 — 2? > 線程2 — 2? > 主線程 === 3? > 線程1 — 3? > 線程2 — 3? > 主線程 === 4? > 線程1 — 4? > 請按任意鍵繼續… 線程2 — 4? > 線程2 — 5? > 線程2 — 6? > 線程2 — 7? > 線程2 — 8? > 線程2 — 9 為進一步理解線程同步的重要性和互斥量的使用方法,我們再來看一個例子。 買火車票是大家春節回家最為關注的事情,我們就簡單模擬一下火車票的售票系統(為使程序簡單,我們就抽出最簡單的模型進行模擬):有500張從北京到贛州的火車票,在8個窗口同時出售,保證系統的穩定性和數據的原子性。 ### 【Demo4】:模擬火車售票系統 **SaleTickets.h :** ~~~ #include "stdafx.h" #include <windows.h> #include <iostream> #include <strstream> #include <string> using namespace std; #define NAME_LINE 40 //定義線程函數傳入參數的結構體 typedef struct __TICKET { int nCount; char strTicketName[NAME_LINE]; __TICKET() : nCount(0) { memset(strTicketName, 0, NAME_LINE * sizeof(char)); } }TICKET; typedef struct __THD_DATA { TICKET* pTicket; char strThreadName[NAME_LINE]; __THD_DATA() : pTicket(NULL) { memset(strThreadName, 0, NAME_LINE * sizeof(char)); } }THD_DATA; //基本類型數據轉換成字符串 template<class T> string convertToString(const T val) { string s; std::strstream ss; ss << val; ss >> s; return s; } //售票程序 DWORD WINAPI SaleTicket(LPVOID lpParameter); ~~~ **SaleTickets.cpp :** ~~~ #include "stdafx.h" #include <windows.h> #include <iostream> #include "SaleTickets.h" using namespace std; extern HANDLE g_hMutex; //售票程序 DWORD WINAPI SaleTicket(LPVOID lpParameter) { THD_DATA* pThreadData = (THD_DATA*)lpParameter; TICKET* pSaleData = pThreadData->pTicket; while(pSaleData->nCount > 0) { //請求獲得一個互斥量鎖 WaitForSingleObject(g_hMutex, INFINITE); if (pSaleData->nCount > 0) { cout << pThreadData->strThreadName << "出售第" << pSaleData->nCount -- << "的票,"; if (pSaleData->nCount >= 0) { cout << "出票成功!剩余" << pSaleData->nCount << "張票." << endl; } else { cout << "出票失敗!該票已售完。" << endl; } } Sleep(10); //釋放互斥量鎖 ReleaseMutex(g_hMutex); } return 0L; } ~~~ 測試程序: ~~~ //售票系統 void Test2() { //創建一個互斥量 g_hMutex = CreateMutex(NULL, FALSE, NULL); //初始化火車票 TICKET ticket; ticket.nCount = 100; strcpy(ticket.strTicketName, "北京-->贛州"); const int THREAD_NUMM = 8; THD_DATA threadSale[THREAD_NUMM]; HANDLE hThread[THREAD_NUMM]; for(int i = 0; i < THREAD_NUMM; ++ i) { threadSale[i].pTicket = &ticket; string strThreadName = convertToString(i); strThreadName = "窗口" + strThreadName; strcpy(threadSale[i].strThreadName, strThreadName.c_str()); //創建線程 hThread[i] = CreateThread(NULL, NULL, SaleTicket, &threadSale[i], 0, NULL); //請求獲得一個互斥量鎖 WaitForSingleObject(g_hMutex, INFINITE); cout << threadSale[i].strThreadName << "開始出售 " << threadSale[i].pTicket->strTicketName << " 的票..." << endl; //釋放互斥量鎖 ReleaseMutex(g_hMutex); //關閉線程 CloseHandle(hThread[i]); } system("pause"); } ~~~ 結果: > 窗口0開始出售 北京–>贛州 的票…? > 窗口0出售第100的票,出票成功!剩余99張票.? > 窗口1開始出售 北京–>贛州 的票…? > 窗口1出售第99的票,出票成功!剩余98張票.? > 窗口0出售第98的票,出票成功!剩余97張票.? > 窗口2開始出售 北京–>贛州 的票…? > 窗口2出售第97的票,出票成功!剩余96張票.? > 窗口1出售第96的票,出票成功!剩余95張票.? > 窗口0出售第95的票,出票成功!剩余94張票.? > 窗口3開始出售 北京–>贛州 的票…? > 窗口3出售第94的票,出票成功!剩余93張票.? > 窗口2出售第93的票,出票成功!剩余92張票.? > 窗口1出售第92的票,出票成功!剩余91張票.? > 窗口0出售第91的票,出票成功!剩余90張票.? > 窗口4開始出售 北京–>贛州 的票…? > 窗口4出售第90的票,出票成功!剩余89張票.? > 窗口3出售第89的票,出票成功!剩余88張票.? > 窗口2出售第88的票,出票成功!剩余87張票.? > 窗口1出售第87的票,出票成功!剩余86張票.? > 窗口0出售第86的票,出票成功!剩余85張票.? > 窗口5開始出售 北京–>贛州 的票…? > 窗口5出售第85的票,出票成功!剩余84張票.? > 窗口4出售第84的票,出票成功!剩余83張票.? > 窗口3出售第83的票,出票成功!剩余82張票.? > 窗口2出售第82的票,出票成功!剩余81張票.
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看