Win32 多线程学习总结
生活随笔
收集整理的這篇文章主要介紹了
Win32 多线程学习总结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Win32多線程編程學習心得
http://blog.csdn.net/jonathan321/article/details/50782832博客原文地址:http://jerkwisdom.github.io/study/thread/thread-Summary/
此處博客不再更新。
為什么多線程?
多線程并不一定是最好的,合適才是最好的。
多線程主要的優點是價廉物美,啟動快、退出快、與其他線程共享核心對象,很容易實現共產主義的偉大夢想。但是其又有不可預期、測試困難的缺點。
使用好多線程,就是要知道何時應該用多線程,何時不該用。如果應該用多線程,如何解決Race Condition問題?如何共享數據?如何提高效率?如何同步線程和數據?總結起來就是:
有始有終,線程的創建和釋放都要靠自己
不拋棄不放棄,等一等線程,讓它做完自己的工作
文明有序,資源占用無沖突
但是有時候卻不建議使用多線程:
針對于慢速I/O設備,Overlapped I/O更能勝任
程序的健壯性要求很高,值得付出比較多的額外負擔,多進程可能更能勝任
操作線程
如何創建線程?
如果要寫一個多線程程序,第一步就是創建一個線程,我們可以使用CreateThread API函數,也可以使用_beginthreadex C 函數,其實我大多數時候使用的是Boost庫上面的boost::thread對象來創建
線程對象。如果有興趣可以看看Boost庫,這里暫且不討論Boost庫thread。
如果使用上面兩個函數,可以去msdn查看。使用上面兩種函數創建線程,其線程函數都必須符合以下格式,當然函數名可以更換:
DWORD WINAPI ThreadFunc(LPVOID n);
使用CreateThread API函數或者_beginthreadex函數,可以傳回兩個值用以識別一個新的線程——返回值Handle(句柄)和輸出參數lpThread(線程ID)。為了安全防護的緣故,不能根據一個線程
的ID獲得其handle。
如何釋放線程?
線程和進程一樣,都是核心對象。如何釋放線程屬于如何釋放核心對象的問題。CloseHandle函數在這里起了十分重要的作用。CloseHandle函數的功能是將核心對象的引用計數減1。其不能直接用來
釋放核心對象,核心對象只有在其引用計數為0的時候會被操作系統自動銷毀。
BOOL CloseHandle(HANDLE hObject);
如果你不調用該函數,即使線程在創建之后執行完畢,引用計數還是不為0,線程無法被銷毀。如果一個進程沒有在結束之前對它所打開的核心對象調用CloseHandle,操作系統會自動把那些對象的引
用計數減一。雖然操作系統會做這個工作,但是他不知道核心對象實際的意義,也就不可能知道解構順序是否重要。如果你在循環結構創建了核心對象而沒有CloseHandle,好吧!你可能會有幾十萬個
句柄沒有關閉,你的系統會因此沒有可用句柄,然后各種異常現象就出現了。記住當你完成你的工作,應該調用CloseHandle函數釋放核心對象。
在清理線程產生的核心對象時也要注意這個問題。不要依賴因線程結束而清理所有被這一線程產生的核心對象。面對一個打開的對象,區分其擁有者是進程或是線程是很重要的。這決定了系統何時做清
理工作。程序員不能選擇有進程或者線程擁有對象,一切都得視對象類型而定。如果被線程打開的核心對象被進程擁有,線程結束是無法清理這些核心對象的。
線程核心對象與線程
其實這兩個是不同的概念。CreateThread函數返回的句柄其實是指向線程核心對象,而不是直接指向線程本身。在創建一個新的線程時,線程本身會開啟線程核心對象,引用計數加1,CreateThread
函數返回一個線程核心對象句柄,引用計數再加1,所以線程核心對象一開始引用計數就是2。
調用CloseHandle函數,該線程核心對象引用計數減一,線程執行完成之后,引用計數再減一為零,該核心對象被自動銷毀。
結束主線程
首先得了解哪個線程是主線程:程序啟動后就執行的線程。主線程有兩個特點:
負責GUI主消息循環
主線程結束時,強迫其他所有線程被迫結束,其他線程沒有機會執行清理工作
第二個特點也就意味著,如果你不等待其他線程結束,它們沒有機會執行完自己的操作,也沒有機會做最后的cleanup操作。我遇到過由于沒有等待,而出現程序奔潰的情況。反正很危險。
結束線程并獲取其結束代碼
這個沒什么好說的,可以使用ExitThread函數退出線程,返回一個結束代碼。GetExitCodeThread函數獲取ExitThread函數或者return語句返回的結束代碼。不過想通過GetExitCodeThread來等待線
程結束是個很糟糕的注意——CPU被浪費了。下一節提及的WaitForSingleObject才是正道。
終止其他線程
終止其他線程可以使用TerminateThread()函數,也可以使用全局標記。
TerminateThread()函數的缺點是:
1、線程沒有機會在結束前清理自己,其堆棧也沒有被釋放掉,出現內存泄露;
2、任何與此線程有附著關系的DLLs也沒有機會獲得線程解除附著通知;
3、線程進入的Critical Section將永遠處于鎖定狀態(Mutex會返回wait_abandoned狀態)。
4、線程正在處理的數據會處于不穩定狀態。
TerminateThread()唯一可以預期的是:線程handle變成激發狀態,并且傳回dwExitCode所指定的結束代碼。
設立全局標記的優點:保證目標線程在結束之前安全而一致的狀態
設立全局標記的缺點:線程需要一個polling機制,時時檢查標記值。(可以使用一個手動重置的event對象)
等一等線程
等待一個線程的結束
使用WaitForSingleObject最顯而易見的好處是你終于可以把以下代碼精簡成一句了。
for(;;) {int rc;rc = GetExitCodeThread(hThread, &exitCode);if(!rc && exitCode != STILL_ACTIVE)break; } → → → → → → WaitForSingleObject(hThread, INFINITE);
其他好處就是: busy loop浪費太多CPU時間
可以設定等待時間
等待多個線程的結束
WaitForSingleObject函數不好同時判斷多個線程的狀態,WaitForMultipleObjects可以同時等待多個線程,可以設定是否等待所有線程執行結束還是只要一個線程執行完立馬返回。
在GUI線程中等待
在GUI線程中總是要常常回到主消息循環,上述兩個wait api函數會阻塞主消息循環。MsgWaitForMultipleObjects函數可以在對象唄激發或者消息到達時被喚醒而返回。
線程同步
線程同步主要有Critical Sections、Mutex、Semaphores、Event,除了Critical Section是存在于進程內存空間內,其他都是核心對象。
Critical Sections
Critical Section用來實現排他性占有,適用范圍時單一進程的各個線程之間。
使用示例:
CRITICAL_SECTION cs ; // here must be global attributes to related thread InitializeCriticalSection (&cs ); EnterCriticalSection(&cs ); LeaveCriticalSection(&cs ); DeleteCriticalSection(&cs );
Critical Sections注意事項:
一旦線程進入一個Critical Section,再調用LeaveCriticalSection函數之前,就能一直重復的進入該Critical Section。
千萬不要在一個Critical section之中調用Sleep()或者任何Wait... API函數。
如果進入Critical section的那個線程結束了或者當掉了,而沒有調用LeaveCriticalSection函數,系統就沒有辦法將該Critical Section清除。
Critical Section的優點:
相對于Mutex來說,其速度很快。鎖住一個未被擁有的mutex要比鎖住一個未被擁有的critical section,需要花費幾乎100倍時間。(critical section不需要進入操作系統核心)
Critical Section的缺陷:
Critical Section不是核心對象,無法WaitForSingleObject,沒有辦法解決死鎖問題(一個著名的死鎖問題:哲學家進餐問題)
Critical Section不可跨進程
無法指定等待結束的時間長度
不能夠同時有一個Critical section被等待
無法偵測是否已被某個線程放棄
Mutex
Mutex可以在不同的線程之間實現排他性戰友,甚至即使那些線程屬于不同進程。
使用示例:
HANDLE hMutex ; // global attributes hMutex = CreateMutex (NULL, // default event attributesfalse, // default not initially ownedNULL // unnamed); DWORD dwWaitResult = WaitForSingleObject (hMutex , INFINITE ); if (dwWaitResult == WAIT_OBJECT_0 ) {// wait succeed, do what you want... } ReleaseMutex(hMutex );
示例解釋:
1、HMutex在創建時為未被擁有和未激發狀態;2、調用Wait...()函數,線程獲得hMutex的擁有權,HMutex短暫變成激發狀態,然后Wait...()函數返回,此時HMutex的狀態是被擁有和未激發;
3、ReleaseMutex之后,HMutex的狀態變為未被擁有和未激發狀態
Mutex注意事項:
Mutex的擁有權并非屬于哪個產生它的哪個線程,而是那個最后對此mutex進行Wait...()操作并且尚未進行ReleaseMutex()操作的線程。
如果線程擁有一個mutex而在結束前沒有調用ReleaseMutex(),mutex不會被摧毀,取而代之,該mutex會被視為“未被擁有”以及“未被激發”,而下一個等待中的線程會被以
WAIT_ABANDONED_0通知。
Wait...()函數在Mutex處于未被擁有和未被激發狀態時返回。
將CreateMutex的第二個參數設為true,可以阻止race condition,否則調用CreateMutex的線程還未擁有Mutex,發生了context switch,就被別的線程擁有了。
Mutex優點
核心對象,可以調用Wait...() API函數
跨線程、跨進程、跨用戶(將CreateMutex的第三個參數前加上"Global//")
可以具名,可以被其他進程開啟
只能被擁有它的哪個線程釋放
Mutex缺點
等待代價比較大
Semaphores
Semaphore被用來追蹤有限的資源。
和Mutex的對比
mutex是semaphore的退化,令semahpore的最大值為1,那就是一個mutex
semaphore沒有擁有權的概念,也沒有wait_abandoned狀態,一個線程可以反復調用Wait...()函數以產生鎖定,而擁有mutex的線程不論在調用多少次Wait...()函數也不會被阻塞。
在許多系統中都有semaphore的概念,而mutex則不一定。
調用ReleaseSemaphore()的那個線程并不一定是調用Wait...()的那個線程,任何線程都可以在任何時間調用ReleaseSemaphore,解除被任何線程鎖定的Semaphore。
Semaphore優點
核心對象
可以具名,可以被其他進程開啟
可以被任何一個線程釋放
Semaphore缺點
Event
Event通常用于overlapped I/O,或者用來設計某些自定義的同步對象。
使用示例:
HANDLE hEvent ; // global attributes hEvent = CreateEvent (NULL, // default event attributestrue, // mannual resetfalse, // nonsignaledNULL // unnamed);SetEvent(hEvent); PulseEvent(hEvent); DWORD dwWaitResult = WaitForSingleObject (hEvent , INFINITE ); ResetEvent(hEvent); if (dwWaitResult == WAIT_OBJECT_0 ) {// wait succeed, do what you want...ResetEvent(hEvent ); }
示例解釋:
1、CreateEvent默認為非激發狀態、手動重置2、SetEvent把hEvent設為激發狀態
3、在手動重置情況下(bManualReset=true),PulseEvent把event對象設為激發狀態,然而喚醒所有等待中的線程,然后恢復為非激發狀態;
4、在自動重置情況下(bManualReset=false),PulseEvent把event對象設為激發狀態,然而喚醒一個等待中的線程,然后恢復為非激發狀態;
5、ResetEvent將hEvent設為未激發狀態
Event注意事項:
CreateEvent函數的第二個參數bManualReset若為false,event會在變成激發狀態(因而喚醒一個線程)之后,自動重置為非激發狀態;
CreateEvent函數的第二個參數bManualReset若為true,event會在變成激發狀態(因而喚醒一個線程)之后,不會自動重置為非激發狀態,必須要手動ResetEvent;
Event優點:
核心對象
其狀態完全由程序來控制,其狀態不會因為Wait...()函數的調用而改變。
適用于設計新的同步對象
可以具名,可以被其他進程開啟
Event缺點:
要求蘇醒的請求并不會被存儲起來,可能會遺失掉。如果一個AutoReset event對象調用SetEvent或PulseEvent,而此時并沒有線程在等待,這個event會被遺失。如Wait...()函數還沒來得及調用就發
生了Context Switch,這個時候SetEvent,這個要求蘇醒的請求會被遺失,然后調用Wait...()函數線程卡死。
替代多線程
Overlapped I/O
Win32之中三個基本的I/O函數:CreateFile()、ReadFile()和WriteFile()。
設置CreateFile()函數的dwFlagsAndAttributes參數為FILE_FLAG_OVERLAPPED,那么對文件的每一個操作都將是Overlapped。此時可以同時讀寫文件的許多部分,沒有目前的文件位置的概念,每
一次讀寫都要包含其文件位置。
如果發出許多Overlapped請求,那么執行順序無法保證。
Overlapped I/O不能使用C Runtime Library中的stdio.h函數,只能使用ReadFile()和WriteFile()來執行I/O。
Overlapped I/O函數使用OVERLAPPED結構來識別每一個目前正在進行的Overlapped操作,同時在程序和操作系統之間提供了一個共享區域,參數可以在該區域雙向傳遞。
多進程
如果一個進程死亡,系統中的其他進程還是可以繼續執行。多進程程序的健壯性遠勝于多線程。因為如果多個線程在同一個進程中運行,那么一個誤入歧途的線程就可能把整個進程給毀了。
另一個使用多重進程的理由是,當一個程序從一個作業平臺被移植到另一個作業平臺,譬如Unix(不支持線程,但進程的產生與結束的代價并不昂貴),Unix應用程序往往使用多個進程,如果移植成
為多線程模式,可能需要大改。
文獻
Win32 MultiThread Study A - Thread KeyWord
Win32 MultiThread Study B - Thread Usage
Win32 MultiThread Study C - Wait
Win32 MultiThread Study D - Synchronization
Win32 MultiThread Study E - Handle Thread
Win32 MultiThread Study E - Handle Process
歡迎訪問我的個人博客click me
博客原文地址:Win32 MultiThread Study Summary - Let's Thread
========
win32多線程 (一) 線程創建與結束等待
http://www.cnblogs.com/zhidao-chen/p/3851526.html#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;
?
DWORD WINAPI ThreadFuncFirst(LPVOID param)
{
?int iCount = 50;
?while(iCount--){
? cout<<"\nThreadFuncFirst:"<<iCount;
?}
?return 0;
}
DWORD WINAPI ThreadFuncSecond(LPVOID param)
{
?int iCount = 50;
?while(iCount--){
? cout<<"\nThreadFuncSecond:"<<iCount;
?}
?return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
?DWORD dwThreadID = 0;
?HANDLE handleFirst = CreateThread(NULL, 0, ThreadFuncFirst, 0, 0, &dwThreadID);
?if (!handleFirst)
?{
? cout<<"create thread 1 error:"<<endl;
?}
?HANDLE handleSecond = CreateThread(NULL, 0, ThreadFuncSecond, 0, 0, &dwThreadID);
?if (!handleSecond)
?{
? ?cout<<"create thread 2 error:"<<endl;
?}
//HANDLE arrayHandle[] = {handleFirst, handleSecond};
?//WaitForMultipleObjects(2, arrayHandle, TRUE, INFINITE);
?WaitForSingleObject(handleFirst, INFINITE);//等待線程返回,用sleep()就太山寨了
?WaitForSingleObject(handleSecond, INFINITE);
?CloseHandle(handleFirst);//句柄默認值2 這里減1,線程函數執行完后釋放資源。
?CloseHandle(handleSecond);
?return 0;
}
========
Win32 多線程和線程同步
http://blog.csdn.net/zuishikonghuan/article/details/48208357版權聲明:本文為博主原創文章,本文作者授權在知識共享"署名-非商業性使用-相同方式共享" 4.0 (CC BY-NC-SA 4.0) 許可證下發布,您可以自由地在任何媒介以任何形式復制、發行本作品、修改、
轉換或以本作品為基礎進行創作;您將必須同樣提供原作者信息以及協議聲明,您不得將本作品用于商業目的,并且您只能采用與本協議相同的許可協議發布基于本作品的演繹作品。
本博文由CSDN博主zuishikonghuan所作,版權歸zuishikonghuan所有,轉載請注明出處:http://blog.csdn.net/zuishikonghuan/article/details/48208357
多線程:一個進程創建時,默認情況下系統會為它創建一個主線程,(如果使用Native API創建的線程就沒有主線程,是空的,必須自己創建主線程),應用程序可以自己創建線程,還有以前寫過的一
篇“DLL注入技術”,就是遠程在其他進程中創建線程,然后讓遠程線程load我們的dll。
系統是如何實現多線程的?其實,對于單CPU單核心的設備上,在一個確定的時刻,只能執行內存中的一個指令。所謂的“多任務搶占式操作系統”,其實是將CPU劃分了“時間段”,并分配給每一
個線程,系統的任務調度程序會根據時間段切換線程上下文和進程上下文(比如線程的寄存器和狀態等等信息就存儲在上下文中),這個時間段不能太短,否則一個線程還沒干什么事呢就切換走了,浪
費效率,更不能太長,否則用戶就感覺程序不是同時運行的。
創建一個線程,標準的Win32 API是CreateThread。
CreateThread函數:
[cpp] view plain copy
HANDLE WINAPI CreateThread( ?
? _In_opt_ ?LPSECURITY_ATTRIBUTES ?lpThreadAttributes, ?
? _In_ ? ? ?SIZE_T ? ? ? ? ? ? ? ? dwStackSize, ?
? _In_ ? ? ?LPTHREAD_START_ROUTINE lpStartAddress, ?
? _In_opt_ ?LPVOID ? ? ? ? ? ? ? ? lpParameter, ?
? _In_ ? ? ?DWORD ? ? ? ? ? ? ? ? ?dwCreationFlags, ?
? _Out_opt_ LPDWORD ? ? ? ? ? ? ? ?lpThreadId ?
); ?
第1個參數:線程內核對象的安全屬性,一般置NULL,使用默認設置。
第2個參數:線程棧空間大小。0表示使用默認大小。
第3個參數:新線程所執行的線程函數地址。
線程函數原型:
[cpp] view plain copy
DWORD WINAPI ThreadProc( ?
? _In_ LPVOID lpParameter ?
); ?
lpParameter:通過CreateThread傳人的第四個參數。
第4個參數:傳給線程函數的參數。
第5個參數:為0表示線程創建之后直接運行,CREATE_SUSPENDED表示線程創建后暫停運行,通過調用ResumeThread事線程運行。
第6個參數:返回線程的ID。
返回值:成功返回新線程的句柄,失敗返回NULL。
特別說明:CreateThread后,如果不需要操作線程,可以直接CloseHandle掉這個線程句柄,關閉這個線程句柄不會影響線程運行。
關于_beginthreadex函數:
另外還有一個_beginthreadex函數,也是用來創建線程的
很多資料都一再強調“應該使用_beginthreadex函數,不要使用CreateThread函數”,這樣說其實是有道理的。
MSDN上說:
A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and?
ExitThread; this requires the use of the multithreaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory?
conditions.
因為_beginthreadex設計出來的目的就是為了使C/C++的運行時函數支持多線程的,它先構建了一個C/C++的運行時函數的環境,之后從內部調用了CreateThread。因此如果不需要使用C/C++的運
行時函數,那么,建議使用CreateThread而不要使用_beginthreadex!因為_beginthreadex會造成CPU和內存資源浪費,如果不需要使用C/C++的運行時函數,在對程序效率要求很高的條件下,應
該使用CreateThread。
其實,絕大多數C/C++的運行時函數都是調用了系統的API!
關于關閉一個線程:
在用戶模式下(比如在Win32子系統下)關閉線程的正確做法是讓線程自己返回,強制結束線程是不可取的。
在內核模式下(驅動程序),還需要做一些其他工作。
線程同步:
當我們創建了多個線程的時候,如果線程訪問同一個資源,結果會如何?
假設我們一個全局變量x,創建了兩個線程a,b。a和b都要讀寫x,結果就無法得知了,為何?因為系統會隨時調度線程,而又有兩個很難克服的原因:
1。對變量的操作,并非是直接訪問內存,而是先把內存單元讀入寄存器,修改寄存器,然后把寄存器的數據寫回內存。
如果線程a剛把x讀入寄存器,這時候系統把CPU調度到b上了,b改完x,回到a,a無法知道內存已經變了,于是把寄存器改了之后寫入內存,這樣就造成了線程b做了無用功!以后程序也可能會出現問
題。
不信可以反匯編你的程序看看匯編代碼。。
2。CPU讀寫內存不是直接讀寫的,其實,CPU為了提高連續讀寫內存的效率,引入了一個“高速緩存行”,是將一段內存讀到緩存行中再寫回內存,因此如果線程調度剛調度到讀入緩存行時切換走線
程上下文的話和上面的問題一樣。
所以我們需要進行“線程同步”,實現原子訪問。
用戶模式下常見的線程同步的方法有:事件(Event)、互斥體(Mutex)、信號量(Semaphore)等
其中最常用的是互斥體
關于互斥體:當一個線程獲取了互斥體,那么其他線程就不能獲取,一個互斥體只能同時被一個線程獲得,而其他試圖獲取互斥體的線程將會等待互斥體的釋放,釋放后,再有一個線程獲取互斥體。
這樣,我們就可以在線程訪問同一個資源時獲取互斥體,訪問完了釋放,就可以避免上面的問題了,這就是線程同步。
創建互斥體 CreateMutex:
[cpp] view plain copy
HANDLE WINAPI CreateMutex( ?
? _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, ?
? _In_ ? ? BOOL ? ? ? ? ? ? ? ? ?bInitialOwner, ?
? _In_opt_ LPCTSTR ? ? ? ? ? ? ? lpName ?
); ?
參數1:安全屬性,NULL表示默認
參數2:是否被占有。
參數3:命名
返回值:成功返回互斥體的句柄
釋放互斥體 ReleaseMutex:
[cpp] view plain copy
BOOL WINAPI ReleaseMutex( ?
? _In_ HANDLE hMutex ?
); ?
參數:互斥體句柄
得到互斥體 WaitForSingleObject:
[cpp] view plain copy
DWORD WINAPI WaitForSingleObject( ?
? _In_ HANDLE hHandle, ?
? _In_ DWORD ?dwMilliseconds ?
); ?
參數1:要等待對象的句柄(要獲取的互斥體的句柄)
參數2:超時間隔,以毫秒為單位。如果指定一個非零值,則該函數等待,直到該對象處于終止狀態或到達時間間隔。INFINITE表示一直等待到對象處于終止狀態。
等待線程完成:使用WaitForSingleObject等待線程句柄即可。
例子:
有必要說一句,這里因為只有兩個線程,每個線程只操作一次,效果不明顯,你可以用for循環,讓兩個個線程分別輸出不同的內容很多次,那么加不加線程同步效果就很明顯了。
[cpp] view plain copy
#include <stdio.h> ?
#include <Windows.h> ?
??
int x = 0; ?
??
DWORD WINAPI ThreadProc(LPVOID lpParameter){ ?
? ? //獲取主線程傳來的互斥體句柄 ?
? ? HANDLE* pMutex = (HANDLE*)lpParameter; ?
? ? //獲取互斥體,如果被其他線程獲取了就一直等待下去 ?
? ? WaitForSingleObject(*pMutex, INFINITE); ?
? ? //這些操作可視為原子訪問 ?
? ? x++; ?
? ? //釋放互斥體 ?
? ? ReleaseMutex(*pMutex); ?
? ? return 0; ?
} ?
??
int _tmain(int argc, _TCHAR* argv[]) ?
{ ?
? ? //創建互斥體 ?
? ? HANDLE hMutex = CreateMutex(NULL, FALSE, TEXT("MyMutex1")); ?
??
? ? //創建線程 ?
? ? HANDLE t1 = CreateThread(NULL, 0, ThreadProc, &hMutex, 0, NULL); ?
? ? HANDLE t2 = CreateThread(NULL, 0, ThreadProc, &hMutex, 0, NULL); ?
??
? ? //等待線程退出 ?
? ? WaitForSingleObject(t1, INFINITE); ?
? ? WaitForSingleObject(t2, INFINITE); ?
??
? ? //關閉句柄,釋放資源 ?
? ? CloseHandle(hMutex); ?
??
? ? printf("%d", x); ?
? ? getchar(); ?
? ? return 0; ?
} ?
========
Win32多線程 創建線程、獲取線程執行狀態、退出線程、錯誤處理
http://blog.csdn.net/xiaoding133/article/details/7770579產生一個線程:
[cpp] view plain copy
HANDLE CreateThread( ?
? LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD描述施行與這一新線程的security屬性,NULL表示使用缺省值,在windows 95中忽略該參數 ?
? DWORD dwStackSize, ? ? ? ? ? ? ? ? ? ? ? ?// initial stack size新線程擁有的堆棧大小,0表示缺省大小,1MB ?
? LPTHREAD_START_ROUTINE lpStartAddress, ? ?// thread function ?函數指針 ?
? LPVOID lpParameter, ? ? ? ? ? ? ? ? ? ? ? // thread argument ?傳遞到線程函數的參數 ?
? DWORD dwCreationFlags, ? ? ? ? ? ? ? ? ? ?// creation option ?允許產生一個暫時掛起的線程,默認為立即運行 ?
? LPDWORD lpThreadId ? ? ? ? ? ? ? ? ? ? ? ?// thread identifier ?//新的線程ID會被傳回到這里 ?
); ?
第一個使用線程范例:
//多線程出現的執行混亂問題
[cpp] view plain copy
#define WIN32_LEAN_AND_MEAN ?
#include<stdio.h> ?
#include<stdlib.h> ?
#include<windows.h> ?
? ??
DWORD WINAPI ThreadFunc(LPVOID); //線程標準函數形式 ?
//#define ?WINAPI _stdcall ?
? ??
int main() ?
{ ?
? HANDLE hThrd; ?
? DWORD threadId; ?
? int i; ?
? for(i=0;i<5;i++) ?
? { ?
? ? ? hThrd=CreateThread(NULL,0,ThreadFunc,(LPVOID)i,0,&threadId); ?
? ? ?//返回一個核心對象hThrd ?
? ? ? if(hThrd) ?
? ? ? { ?
? ? ? ? ? printf("Thread Start %d\n",i); ?
? ? ? ? ? CloseHandle(hThrd); ?
? ? ? } ?
? } ?
? ??
? Sleep(2000); //等待這些線程完成,不加這句,主線程將結束,其他線程將無法完成 ?
? return EXIT_SUCCESS; ?
} ?
? ??
DWORD WINAPI ThreadFunc(LPVOID n) ?
{ ?
? int i; ?
? for(i=0;i<10;i++) ?
? { ?
? ? ? printf("%d%d%d%d%d%d%d%d\n",n,n,n,n,n,n,n,n); ?
? } ?
? return 0; ?
} ?
核心對象:CreateThread()返回的handle被稱為一個核心對象(kernel object).其和所謂的GDI對象,如畫筆、畫刷或DC差不多,前者由KERNEL32.DLL管理,后者由GDI32.DLL管理。所謂handle就是
一個指針,指向操作系統內存空間的某樣東西,那東西不允許你直接 取得。
Win32核心對象清單:
1.進程(processes),線程(threads),文件(files),事件(events),信號量(semaphores),互斥器(mutexes),管道(Pipes,分為named和anonymous兩種)
這些核心對象可以用來整合許多的線程或進程。
GDI對象和核心對象的主要區別:GDI的對象有單一擁護者,不是進程就是線程。核心對象可以有一個以上的擁有者,甚至可以跨進程。
[cpp] view plain copy
BOOL CloseHandle( ?
? HANDLE hObject ? // handle to object ?
); ?
不關閉可能導致資源泄露。不可以依賴“因線程的結束而清理所有被這一線程產生的核心對象”。許多對象,如文件,是被進程所擁有,而非線程擁有,在進程結束之前不能夠清理它們。
線程對象與線程的不同:
線程的handle是指向“線程核心對象”,而不是指向線程本身。當調用CloseHandle()并給一個線程handle時候,就是吧引用計數減1.如果該值變為0,對象會自動被操作系統銷毀。
判斷線程是否結束:
[cpp] view plain copy
BOOL GetExitCodeThread( ?
? HANDLE hThread, ? ? ?// handle to the thread ?
? LPDWORD lpExitCode ? // termination status ?
); ?
如果線程結束,結束代碼會被放在lpExitCode參數中帶回來。如果沒有結束,lpExitCode的值是STILL_ACTIVE.
如果成功,返回True,否則返回False;
GetExitCodeThread將傳回線程函數ThreadFunc的返回值。
GetExitCodeThread等待一個線程的結束,但這并不是最好的方法
第二個線程范例:
//示例GetExitCodeThread的用法,獲取線程的狀態
[cpp] view plain copy
#define WIN32_LEAN_AND_MEAN ?
#include<stdio.h> ?
#include<stdlib.h> ?
#include<windows.h> ?
#include<conio.h> ?
DWORD WINAPI ThreadFunc(LPVOID); //線程標準函數形式 ?
??
int main() ?
{ ?
? ? ??
? HANDLE hThrd1; ?
? HANDLE hThrd2; ?
? DWORD exitCode1=0; ?
? DWORD exitCode2=0; ? ?
??
? DWORD threadId; ? ?
??
? hThrd1=CreateThread(NULL,0,ThreadFunc,(LPVOID)1,0,&threadId); ? ?
??
? if(hThrd1) ?
? ? ? printf("Thread 1 launched \n"); ? ?
??
? hThrd2=CreateThread(NULL,0,ThreadFunc,(LPVOID)2,0,&threadId); ?
? if(hThrd2) ?
? ? ? printf("Thread 2 launched \n"); ? ?
??
? for(;;) ?
? { ?
? ? ? printf("Press any key to exit..\n"); ?
? ? ? getch(); ?
? ? ? ? ? //GetExitCodeThread等待一個線程的結束,但這并不是最好的方法 ?
? ? ? GetExitCodeThread(hThrd1,&exitCode1); //GetExitCodeThread將傳回線程函數ThreadFunc的返回值 ?
? ? ? GetExitCodeThread(hThrd2,&exitCode2); ? ?
??
? ? ? if(exitCode1==STILL_ACTIVE) ?
? ? ? ? ? puts("Thread 1 is still running ..."); ?
? ? ? if(exitCode2==STILL_ACTIVE) ?
? ? ? ? ? puts("Thread 2 is still running ..."); ? ?
??
? ? ? if(exitCode1!=STILL_ACTIVE&&exitCode2!=STILL_ACTIVE) ?
? ? ? ? ? break; ? ?
? } ?
? CloseHandle(hThrd1); ?
? CloseHandle(hThrd2); ?
? printf("Thread 1 returned %d\n",exitCode1); ?
? printf("Thread 2 returned %d\n",exitCode2); ?
? ??
?return ?EXIT_SUCCESS; ?
} ?
DWORD WINAPI ThreadFunc(LPVOID n) ?
{ ?
? ? Sleep((DWORD)n*1000*2); ?
? ? return (DWORD)n*2; ?
} ?
結束一個線程:
前面是靠線程函數的結束而結束線程。有時候需要更強制性的手法結束一個線程要用ExitThread();
[cpp] view plain copy
VOID ExitThread( ?
? DWORD dwExitCode ? // exit code for this thread 指示此線程之結束代碼 ?
); ?
它可以在任何時候被調用并且絕對不會返回。任何代碼若放在此行之下,保證不會被執行。
如果線程還在運行而我的程序的結束了,會怎么樣??
結束主線程:程序啟動后就執行一個線程稱為主線程,主線程有兩個特點,第一,負責GUI程序中的主消息循環。第二,這一線程的結束(不論是因為返回或因為調用了ExitThread())會使程序中的所
有線程都被強迫結束,程序也因此結束。其他線程沒有機會做清理工作。故在main或WinMain結束之前,總是先等待所有的線程都結束。
建議在主線程中不要調用ExitThread();
第三個線程范例:怎么結束一個線程
[cpp] view plain copy
#define WIN32_LEAN_AND_MEAN ?
#include<stdio.h> ?
#include<stdlib.h> ?
#include<windows.h> ?
DWORD WINAPI ThreadFunc(LPVOID); //線程標準函數形式 ?
void AnotherFunc(void); ?
? ??
int main() ?
{ ?
?HANDLE hThrd; ?
?DWORD exitCode=0; ?
?DWORD threadId; ?
?hThrd=CreateThread(NULL,0,ThreadFunc,(LPVOID)1,0,&threadId); ?
? ??
? if(hThrd) ?
? ? ? printf("Thread launched.. \n"); ?
? ??
? for(;;) ?
? { ?
? ? ? bool rc; ?
? ? ? rc=GetExitCodeThread(hThrd,&exitCode); ?
? ? ? if(rc&&exitCode!=STILL_ACTIVE) ?
? ? ? ? ? break; ?
? } ?
? ??
? CloseHandle(hThrd); ?
? printf("Thread ?returned %d\n",exitCode); ?
? ?return ?EXIT_SUCCESS; ?
??
} ?
? ??
DWORD WINAPI ThreadFunc(LPVOID n) ?
{ ?
? printf("Thread running ..\n"); ?
? AnotherFunc(); //調用一個函數,退出該線程 ?
? return 0; ?
} ?
? ??
void AnotherFunc() ?
{ ?
printf("About to exit Thread ..\n"); ?
ExitThread(4); ?
printf("This will never print ..\n"); //這一行不會被打印 ?
} ?
/****************錯誤處理*********************/
可以調用GetLastError獲得描述
什么是MTVERIFY?
它是一個宏,這個宏內部記錄并解釋Win32 GetLastError()的結果。如果Win32函數失敗,MTVERIFY()會打印一段簡短的文字說明。
如何使用MTVERIFY的范例:
錯誤處理函數
[cpp] view plain copy
//示例錯誤處理 ?
#define WIN32_LEAN_AND_MEAN ?
#include<stdio.h> ?
#include<stdlib.h> ?
#include<windows.h> ?
//MTVERIFY宏進入 ?
#include "MtVerify.h" ?
DWORD WINAPI ThreadFunc(LPVOID); ?
??
int main() ?
{ ?
? HANDLE hThrd; ?
?DWORD exitCode=0; ?
?DWORD threadId; ?
?MTVERIFY(hThrd=CreateThread(NULL,0,ThreadFunc,(LPVOID)1,0,&threadId)); ?
??
if(hThrd) ?
? ? ? printf("Thread launched.. \n"); ? ?
??
MTVERIFY(CloseHandle(hThrd)); ?
? ??
for(;;) ?
? { ?
? ? ? bool rc; ?
? ? ? MTVERIFY(rc=GetExitCodeThread(hThrd,&exitCode)); ?
? ? ? if(rc&&exitCode!=STILL_ACTIVE) ?
? ? ? ? ? break; ? ?
? } ?
printf("Thread ?returned %d\n",exitCode); ?
? ? return EXIT_SUCCESS; ? ? ?
} ? ?
??
DWORD WINAPI ThreadFunc(LPVOID n) ?
{ ?
? printf("Thread running ..\n"); ?
? return 0; ?
} ? ?
? ??
/** ??
* <span style="font-size:18px;"><strong>MtVerify.h </strong></span> ?
* ??
* Error handling for applications in ??
* "Multitheading Applications in Win32" ??
* ??
* The function PrintError() is marked as __inline so that it can be ??
* included from one or more C or C++ files without multiple definition ??
* errors. For the examples in this book, this works fine. ??
* To use the PrintError() in an application, it should be taken out, ??
* placed in its own source file, and the "__inline" declaration removed ??
* so the function will be globally available. ??
*/ ? ?
#pragma comment( lib, "USER32" ) ? ??
#include <stdlib.h> ? ??
#include <crtdbg.h> ? ??
#define MTASSERT(a) _ASSERTE(a) ? ??
// 宏定義 __FILE__ 與__LINE__都是預處理符號提供錯誤信息的描述 ? ??
// 如果a返回FALSE就執行PrintError函數 ? ??
#define MTVERIFY(a) if (!(a)) PrintError(#a,__FILE__,__LINE__,GetLastError()) ? ??
__inline void PrintError(LPSTR linedesc, LPSTR filename, int lineno, DWORD errnum) ? ??
{ ? ??
? ? LPSTR lpBuffer; ? ??
? ? char errbuf[256]; ? ??
#ifdef _WINDOWS ? ??
? ? char modulename[MAX_PATH]; ? ??
#else // _WINDOWS ? ??
? ? DWORD numread; ? ??
#endif // _WINDOWS ? ??
? ? // 把從GetLastError()返回的錯誤碼轉化為錯誤信息 ? ? ?
? ? FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER ? ??
? ? ? ? | FORMAT_MESSAGE_FROM_SYSTEM, ? ??
? ? ? ? NULL, ? ??
? ? ? ? errnum, ? ??
? ? ? ? LANG_NEUTRAL, ? ??
? ? ? ? (LPTSTR)&lpBuffer, ? ??
? ? ? ? 0, ? ??
? ? ? ? NULL ); ? ??
? ? wsprintfA(errbuf, "\nThe following call failed at line %d in %s:\n\n" ? ?
? ? ? ? " %s\n\nReason: %s\n", lineno, filename, linedesc, lpBuffer); ? ??
? ? // 如果是console程序就輸出信息到控制臺上 ?
#ifndef _WINDOWS ? ??
? ? WriteFile(GetStdHandle(STD_ERROR_HANDLE), errbuf, strlen(errbuf), &numread, FALSE ); ? ??
? ? // 等待3秒鐘是為了使用者看到出錯信息 ? ??
? ? Sleep(3000); ? ??
? ? // 如果是窗口程序就一彈出對話框的形式輸出錯誤信息 ?
#else ? ??
? ? // 當前exe文件的全路徑 ? ??
? ? GetModuleFileName(NULL, modulename, MAX_PATH); ? ??
? ? // 置彈出窗口在最上層以免被忽略 ? ??
? ? MessageBox(NULL, errbuf, modulename, MB_ICONWARNING|MB_OK|MB_TASKMODAL|MB_SETFOREGROUND); ? ??
#endif ? ??
? ? // 把結束代碼EXIT_FAILURE 交給操作系統 ?
? ? exit(EXIT_FAILURE); ? ??
} ? ?
多線程綜合實例:后臺打印,建立一個Win32 Application?
[cpp] view plain copy
/*多線程后臺打印范例*/ ?
??
??
/******************************/ ?
/*?
?* ?
?*?
?* Sample code for "Multithreading Applications in Win32"?
?* This is from Chapter 2, Listing 2-3?
?*?
?* Demonstrates background printing?
?*/ ?
??
#define WIN32_LEAN_AND_MEAN ?
#include <stdio.h> ?
#include <stdlib.h> ?
#include <windows.h> ?
#include <windowsx.h> ?
#include <commdlg.h> ?
#include "resource.h" ?
#include "MtVerify.h" ?
??
// ?
// Macro definitions ?
// ?
#define WM_SHOWBITMAP ? WM_APP ?
??
#define MAX_PRINT_JOBS ?64 ?
??
// ?
// Structures ?
// ?
typedef struct ?
{ ? // Information passed to background thread for printing ?
? ? HWND hDlg; ?
? ? HWND hWndParent; ?
? ? HDC hDc; ?
? ? BOOL bPrint; ? ?// TRUE if printing; ?
? ? char szText[256]; ?
} ThreadPrintInfo; ?
??
// ?
// Global variables ?
// ?
HANDLE hInst; ?
HBITMAP gbmpDisplay; ?
RECT gDisplayRect; ?
??
int gNumPrinting = 0; ?
??
// Handle to each created thread ?
HANDLE gPrintJobs[64]; //保存已經創建的線程 ?
??
// Height of bitmap returned by DrawText ?
int iHeight; ?
??
// HWND of the dialog so other threads can find it. ?
HWND hDlgMain; ?
??
// ?
// Function declarations ?
// ?
int APIENTRY WinMain(HINSTANCE hInstance/*當前程序運行實例的句柄,每個程序可以運行多個實例*/, ?
? ? ? ? ? ? ? ? ? ?HINSTANCE hPrevInstance, /*當前實例前一個實例的句柄 ,win32下為NULL*/ ?
? ? ? ? ? ? ? ? ? ?LPSTR lpCmdLine, /*指定傳遞給命令行的參數*/ ?
? ? ? ? ? ? ? ? ? ?int nCmdShow/*指定程序窗口應該如何顯示,如最大化等*/); ?
LRESULT CALLBACK MainWndProc(HWND hWnd/*標志接受消息的具體窗口*/, unsigned msg, WPARAM wParam, LPARAM lParam); ?
LRESULT CALLBACK PrintDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); ?
BOOL PrintDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam); ?
void PrintDlg_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify); ?
void PrintDlg_OnPaint(HWND hwnd); ?
void PrintText(HWND hwndParent, char *pszText); ?
void PrintToDisplay(HWND hwndParent, char *pszText); ?
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); ?
DWORD WINAPI BackgroundPrintThread(LPVOID pVoid); ?
?
/// ?
// ?
// ? ? ?WinMain ?
// ?
// Main entry point of application. This will be a ?
// dialog based app, not a normal window, so this ?
// routine acts a little differently than "normal". ?
// ?
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) ?
{ ?
? ? MSG ? ? msg; ?
? ? HWND ? ?hWnd; ?
? ? WNDCLASS wc; ?
? ? int index; ?
??
? ? hInst = hInstance; ?
? ?//1.設計一個窗口類 ?
? ? if (!hPrevInstance) ?
? ? { ?
? ? ? ? memset(&wc, 0, sizeof(wc)); ?
? ? ? ? wc.lpfnWndProc ?= MainWndProc; ?
? ? ? ? wc.hInstance ? ?= hInstance; ?
? ? ? ? wc.hIcon ? ? ? ?= LoadIcon (hInstance, "GenIco"); ?
? ? ? ? wc.hCursor ? ? ?= LoadCursor(NULL,IDC_ARROW); ?
? ? ? ? wc.hbrBackground= GetSysColorBrush(COLOR_BACKGROUND); ?
? ? ? ? wc.lpszMenuName = "PRINTING_MENU"; ?
? ? ? ? wc.lpszClassName= "PrintDlgClass"; ?
? ? ? ? if (!RegisterClass(&wc)) ?
? ? ? ? ? ? return FALSE; ?
? ? } ?
??
//2.創建窗口,并返回創建成功后的窗口句柄 ?
??
? ? hWnd = CreateWindow( ?
? ? ? ? "PrintDlgClass", ?
? ? ? ? "Background Printing", ?
? ? ? ? WS_OVERLAPPED|WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU, ?
? ? ? ? CW_USEDEFAULT, // At this point we do not want to ?
? ? ? ? 0, ? ? ? ? ? ? // ?show the window until we know ?
? ? ? ? 0, ? ? ? ? ? ? // ?how big the Dialog Box is so ?
? ? ? ? 0, ? ? ? ? ? ? // ?that we can fit the main window ?
? ? ? ? NULL, ? ? ? ? ?// ?around it. ?
? ? ? ? NULL, ?
? ? ? ? hInstance, ?
? ? ? ? NULL); ?
??
? ? ? ? //創建打印對話框 ?
? ? hDlgMain = CreateDialog(hInst, ?
? ? ? ? ? ? ? ? ? ? MAKEINTRESOURCE(IDD_PRINT), ?
? ? ? ? ? ? ? ? ? ? hWnd, PrintDlgProc); ?
??
??
? ? //3.顯示及刷新窗口 ?
? ? ShowWindow(hWnd, nCmdShow); ?
? ? ShowWindow(hDlgMain, SW_SHOW); ?
? ? //4.消息循環 ?
??
? ? while (GetMessage(&msg, NULL, 0, 0)) ?
? ? { ? // Get Next message in queue ?
? ? ? ? if(hDlgMain == NULL || !IsDialogMessage(hDlgMain,&msg)) ?
? ? ? ? { ?
? ? ? ? ? ? TranslateMessage(&msg); /* Translate virtual key codes */ ?
? ? ? ? ? ? DispatchMessage(&msg); ?/* Dispatches message to window */ ?
? ? ? ? } ?
? ? } // end while ?
??
? ? // Wait for all threads to terminate. The Window will ?
? ? // have already disappeared by this point. ?
? ? //等待各個線程結束 ?
? ? for (index = 0; index < gNumPrinting; index++) ?
? ? { ?
? ? ? ? DWORD status; ?
? ? ? ? do ??
? ? ? ? { ? // Wait for thread to terminate ?
? ? ? ? ? ? GetExitCodeThread(gPrintJobs[index], &status); ?
? ? ? ? ? ? Sleep(10); ?
? ? ? ? } while (status == STILL_ACTIVE); ?
??
? ? } // end for ?
??
? ? return (msg.wParam); ?/* Returns the value from PostQuitMessage */ ?
} ?
?
//主窗口回調函數 ?
LRESULT CALLBACK MainWndProc(HWND hWnd, unsigned msg, WPARAM wParam, LPARAM lParam) ?
{ ?
? ? switch (msg) ?
? ? { ?
? ? case WM_CREATE: ?
? ? ? ? break; ?
??
? ? case WM_COMMAND: ?
??
? ? ? ? switch (wParam) ?
? ? ? ? { ?
? ? ? ? case IDM_ABOUT: ?
? ? ? ? ? ? DialogBox(hInst, "AboutBox", hWnd, (DLGPROC)About); ?
? ? ? ? ? ? break; ?
? ? ? ? case IDM_EXIT: ?
? ? ? ? ? ? PostQuitMessage(0); ?
? ? ? ? ? ? break; ?
? ? ? ? default: ?
? ? ? ? ? ? return (DefWindowProc(hWnd, msg, wParam, lParam)); ?
? ? ? ? } ?
??
? ? case WM_SETFOCUS: ?
? ? ? ? // ensure that the Dialog Box has the focus ?
? ? ? ? SetFocus(hDlgMain); ?
? ? ? ? break; ?
??
? ? case WM_DESTROY: ?
? ? ? ? PostQuitMessage(0); ?
? ? ? ? break; ?
??
? ? default: ?
? ? ? ? return DefWindowProc(hWnd, msg, wParam, lParam); ?
??
? ? } ?
? ? return 0; ?
} ? ?
??
LRESULT CALLBACK PrintDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) ?
{ ?
? ? switch (uMsg) ?
? ? { ?
? ? case WM_CLOSE: ?
? ? ? ? DestroyWindow(hDlg); ?
? ? ? ? hDlgMain = NULL; ?
? ? ? ? break; ?
? ? ? ? ??
? ? case WM_DESTROY: ?
? ? ? ? return TRUE; ?
? ? ? ? break; ?
??
? ? case WM_SHOWBITMAP: ?
? ? ? ? if (gbmpDisplay) ?
? ? ? ? ? ? DeleteObject(gbmpDisplay); ?
??
? ? ? ? gDisplayRect = *(RECT*)wParam; ?
? ? ? ? gbmpDisplay = (HBITMAP) lParam; ?
? ? ? ? InvalidateRect(hDlgMain, NULL, TRUE); ?
? ? ? ? break; ?
??
? ? HANDLE_MSG(hDlg, WM_INITDIALOG, PrintDlg_OnInitDialog); ?
? ? HANDLE_MSG(hDlg, WM_COMMAND, PrintDlg_OnCommand); ?
? ? HANDLE_MSG(hDlg, WM_PAINT, PrintDlg_OnPaint); ?
??
? ? default: ?
? ? ? ? return (FALSE); ?
? ? } ?
??
? ? return 0; ?
} ?
??
BOOL PrintDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam) ?
{ ?
? ? RECT rect; ?
??
? ? // Size parent to fit this dialog ?
? ? GetWindowRect(hwndDlg, &rect); ??
? ? SetWindowPos(GetParent(hwndDlg),NULL, ?
? ? ? ? 0,0, ?
? ? ? ? rect.right-rect.left, ?
? ? ? ? rect.bottom-rect.top+GetSystemMetrics(SM_CYMENU) ?
? ? ? ? ? ? +GetSystemMetrics(SM_CYCAPTION), ?
? ? ? ? SWP_NOMOVE | SWP_NOZORDER); ?
??
? ? return TRUE; ?
} ?
??
void PrintDlg_OnCommand(HWND hDlg, int id,HWND hwndCtl, UINT codeNotify) ?
{ ?
? ? char szText[256]; ?
??
? ? switch (id) ?
? ? { ?
? ? case IDC_PRINT: ?
? ? ? ? GetDlgItemText(hDlg, IDC_EDIT_TEXT, szText, 256); ?
? ? ? ? PrintText(hDlg, szText); ?
? ? ? ? break; ?
??
? ? case IDC_DISPLAY: ?
? ? ? ? GetDlgItemText(hDlg, IDC_EDIT_TEXT, szText, 256); ?
? ? ? ? PrintToDisplay(hDlg, szText); ?
? ? ? ? break; ?
??
? ? case IDCANCEL: ?
? ? case IDM_EXIT: ?
? ? ? ? PostMessage(GetParent(hDlg),WM_DESTROY, ?
? ? ? ? ? ? ? ? ? ? ? ? (WPARAM)0, (LPARAM)0); ?
? ? ? ? DestroyWindow(hDlgMain); ?
? ? ? ? hDlgMain = NULL; ?
? ? ? ? break; ?
? ? ? ? ??
? ? default: ?
? ? ? ? break; ?
? ? } ?
} ?
??
void PrintDlg_OnPaint( HWND hwnd ) ?
{ ?
? ? PAINTSTRUCT paint; ?
? ? HWND hwndCtrl; ?
? ? HDC hdc; ?
? ? HDC hDcMem; ?
? ? HBITMAP bmpOld; ?
? ? RECT rect; ?
? ? POINT point; ?
??
? ? if (!gbmpDisplay) ?
? ? ? ? return; ?
??
? ? hwndCtrl = GetDlgItem(hwnd, IDC_OUTPUT); ?
??
? ? hdc = BeginPaint(hwnd, &paint); ?
??
? ? GetWindowRect(hwndCtrl, &rect); ?
? ? point = *((POINT *)&rect); ?
? ? ScreenToClient(hwnd, &point); ?
??
? ? hDcMem = CreateCompatibleDC(NULL); ?
? ? bmpOld = SelectObject(hDcMem, gbmpDisplay); ?
??
? ? // Copy bitmap to screen ?
? ? MTVERIFY( BitBlt(hdc, point.x+10, point.y+40, ?
? ? ? ? gDisplayRect.right-gDisplayRect.left, gDisplayRect.bottom-gDisplayRect.top, ?
? ? ? ? hDcMem, iHeight, 0, SRCCOPY) ); ?
??
? ? SelectObject(hDcMem, bmpOld); ?
? ? DeleteDC(hDcMem); ?
??
? ? EndPaint(hwnd, &paint); ?
} ?
??
// ?
// Asks user which printer to use, then creates ?
// background printing thread. ?
// ?
void PrintText(HWND hwndParent, char *pszText) ?
{ ?
? ? ThreadPrintInfo *pInfo; ?
? ? HANDLE hThread; ?
? ? DWORD dwThreadId; ?
? ? int result; ?
? ? DOCINFO docInfo; ?
??
? ? PRINTDLG dlgPrint; ?
??
? ? // Put up Common Dialog for Printing and get hDC. ?
? ? memset(&dlgPrint, 0, sizeof(PRINTDLG)); ?
? ? dlgPrint.lStructSize = sizeof(PRINTDLG); ?
? ? dlgPrint.hwndOwner = hwndParent; ?
? ? dlgPrint.Flags = PD_ALLPAGES | PD_USEDEVMODECOPIES ?
? ? ? ? ? ?| PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC; ?
? ? dlgPrint.hInstance = hInst; ?
? ? if (!PrintDlg(&dlgPrint)) ?
? ? ? ? return; ?
??
? ? // Initialize Printer device ?
? ? docInfo.cbSize = sizeof(DOCINFO); ?
? ? docInfo.lpszDocName = "Background Printing Example"; ?
? ? docInfo.lpszOutput = NULL; ?
? ? docInfo.lpszDatatype = NULL; ?
? ? docInfo.fwType = 0; ?
? ? result = StartDoc(dlgPrint.hDC, &docInfo); ?
? ? result = StartPage(dlgPrint.hDC); ?
??
? ? pInfo = HeapAlloc(GetProcessHeap(), ?
? ? ? ? ? ? ? ? ? ? ? HEAP_ZERO_MEMORY, ?
? ? ? ? ? ? ? ? ? ? ? sizeof(ThreadPrintInfo)); ?
? ? pInfo->hDlg = hwndParent; ?
? ? pInfo->hWndParent = hwndParent; ?
? ? pInfo->hDc = dlgPrint.hDC; ?
? ? pInfo->bPrint = TRUE; ?
? ? strcpy(pInfo->szText, pszText); ?
??
? ? MTVERIFY( hThread = CreateThread(NULL, 0, ?
? ? ? ? BackgroundPrintThread, (LPVOID)pInfo, ?
? ? ? ? 0, &dwThreadId )); ?
??
? ? // keep track of all background printing threads ?
? ? gPrintJobs[gNumPrinting++] = hThread; ?
} ?
??
// ?
// Shows output on the dialog box. ?
// ?
void PrintToDisplay(HWND hwndParent, char *pszText) ?
{ ?
? ? ThreadPrintInfo *pInfo; ?
? ? DWORD dwThreadId; ?
? ? HANDLE hThread; ?
??
? ? pInfo = HeapAlloc(GetProcessHeap(), ?
? ? ? ? ? ? ? ? ? ? ? HEAP_ZERO_MEMORY, ?
? ? ? ? ? ? ? ? ? ? ? sizeof(ThreadPrintInfo)); ?
? ? pInfo->hDlg = hwndParent; ?
? ? pInfo->hWndParent = GetDlgItem(hwndParent, IDC_OUTPUT); ?
? ? pInfo->hDc = GetDC(pInfo->hWndParent); ?
? ? pInfo->bPrint = FALSE; ?
? ? strcpy(pInfo->szText, pszText); ?
??
? ? MTVERIFY( hThread = CreateThread(NULL, 0, ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?BackgroundPrintThread, ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(LPVOID)pInfo, ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0, &dwThreadId )); ?
??
? ? // keep track of all background printing threads ?
? ? gPrintJobs[gNumPrinting++] = hThread; ?
} ?
//--------------------------------------------------------- ?
// About Box Handling ?
//--------------------------------------------------------- ?
??
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) ?
{ ?
? ? switch (message) { ?
? ? ? ? case WM_COMMAND: ?
? ? ? ? ? ? if (LOWORD(wParam) == IDOK ?
? ? ? ? ? ? ? ? || LOWORD(wParam) == IDCANCEL) ?
? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? EndDialog(hDlg, TRUE); ?
? ? ? ? ? ? ? ? return (TRUE); ?
? ? ? ? ? ? } ?
? ? ? ? ? ? break; ?
??
? ? ? ? default: ?
? ? ? ? ? ? return (DefWindowProc(hDlg, message, wParam, lParam)); ?
? ? } ?
??
? ? return FALSE; ?
} ? ?
??
//--------------------------------------------------------- ?
// Background Printing Code后臺線程打印函數 ?
//--------------------------------------------------------- ?
??
DWORD WINAPI BackgroundPrintThread(LPVOID pVoid) ?
{ ?
? ? ThreadPrintInfo *pInfo = (ThreadPrintInfo*) pVoid; ??
? ? RECT rect; ?
? ? RECT rectMem; ?
? ? HDC hDcMem; ?
? ? HBITMAP bmpMem; ?
? ? HBITMAP bmpOld; ?
? ? int x, y; ?
? ? int counter = 0; ?
? ? int nHeight; ?
? ? HFONT hFont; ?
? ? HFONT hFontOld; ?
??
? ? // Get dimensions of paper into rect ?
? ? rect.left = 0; ?
? ? rect.top = 0; ?
? ? rect.right = ?GetDeviceCaps(pInfo->hDc, HORZRES); ?
? ? rect.bottom = GetDeviceCaps(pInfo->hDc, VERTRES); ?
??
? ? nHeight = -MulDiv(36, GetDeviceCaps(pInfo->hDc, LOGPIXELSY), 72); ?
??
? ? // Create Font ?
? ? hFont = CreateFont(nHeight, 0, ??
? ? ? ? 0, 0, FW_DONTCARE, ??
? ? ? ? FALSE, FALSE, FALSE, ??
? ? ? ? ANSI_CHARSET, ??
? ? ? ? OUT_TT_PRECIS, ??
? ? ? ? CLIP_DEFAULT_PRECIS, ?
? ? ? ? PROOF_QUALITY, ??
? ? ? ? VARIABLE_PITCH, ?
? ? ? ? NULL); ?
? ? MTASSERT( hFont != 0); ?
??
? ? // Draw into memory device context ?
? ? hDcMem = CreateCompatibleDC(pInfo->hDc); ?
? ? hFontOld = SelectObject(hDcMem, hFont); ?
? ? iHeight = DrawText(hDcMem, pInfo->szText, -1, ?&rect, DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT); ?
? ? rectMem = rect; ?
? ? rectMem.left = rect.left + iHeight; ?
? ? rectMem.right = rect.right + (iHeight*2); ?
? ? bmpMem = CreateCompatibleBitmap(hDcMem, ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? rectMem.right, rect.bottom); ?
? ? bmpOld = SelectObject(hDcMem, bmpMem); ?
? ? OffsetRect(&rect, iHeight, 0); ??
? ? DrawText(hDcMem, pInfo->szText, -1, ?&rect, ?
? ? ? ? ? ? ?DT_LEFT | DT_NOPREFIX | DT_WORDBREAK); ?
??
? ? // Italicize bitmap. We use GetPixel and ?
? ? // SetPixel because they are horribly inefficient, ?
? ? // thereby causing the thread to run for awhile. ?
? ? for (y = 0; y < iHeight; y++) ?
? ? { ? // Italicize line y ?
? ? ? ? for (x = rectMem.right; x > iHeight; x--) ?
? ? ? ? { ? // Move specified pixel to the right. ?
? ? ? ? ? ? COLORREF color; ?
? ? ? ? ? ? int offset; ?
? ? ? ? ? ? offset = y - iHeight; ?
? ? ? ? ? ? color = GetPixel(hDcMem, x + offset, y); ?
? ? ? ? ? ? if (color != 0) ?
? ? ? ? ? ? ? ? counter++; ?
? ? ? ? ? ? SetPixel(hDcMem, x, y, color); ?
? ? ? ? } // end for x ?
? ? } // end for y ?
? ? MTASSERT( counter > 0); ?
??
? ? // Copy bitmap of italicized text from memory to device ?
? ? if (pInfo->bPrint) ?
? ? { ?
? ? ? ? BitBlt(pInfo->hDc, 50, 50, rectMem.right-rect.left, rectMem.bottom-rect.top, ?
? ? ? ? ? ? hDcMem, iHeight, 0, SRCCOPY); ?
? ? } ?
??
? ? SelectObject(hDcMem, hFontOld); ?
? ? SelectObject(hDcMem, bmpOld); ?
? ? DeleteDC(hDcMem); ?
??
? ? if (!pInfo->bPrint) ?
? ? { ?
? ? ? ? // We can't just write to the global variable where the ?
? ? ? ? // bitmap is kept or we might overwrite the work of ?
? ? ? ? // another thread, thereby "losing" a bitmap ?
??
? ? ? ? // Also, if we used PostMessage instead of SendMessage, then ?
? ? ? ? // the rectangle could have been deleted (it's on the stack) ?
? ? ? ? // by the time the main message loop is reached. ?
? ? ? ? SendMessage(pInfo->hDlg, WM_SHOWBITMAP, (WPARAM)&rectMem, (LPARAM) bmpMem); ?
? ? } ?
??
? ? if (pInfo->bPrint) ?
? ? { ? // Finish printing ?
? ? ? ? int result; ?
??
? ? ? ? result = EndPage(pInfo->hDc); ?
? ? ? ? MTASSERT (result != SP_ERROR); ?
? ? ? ? result = EndDoc(pInfo->hDc); ?
? ? ? ? MTASSERT (result != SP_ERROR); ?
? ? ? ? DeleteDC(pInfo->hDc); ?
? ? ? ? // If we are printing, we are done with the bitmap. ?
? ? ? ? DeleteObject(bmpMem); ?
? ? } ??
? ? else ?
? ? { ?
? ? ? ? ReleaseDC(pInfo->hWndParent, pInfo->hDc); ?
? ? } ?
??
? ? // free data structure passed in. ?
? ? HeapFree(GetProcessHeap(), 0, pInfo); ?
??
? ? return 0; ?
} ?
多線程應該遵循下面原則:
1.各個線程的數據要分離開來,避免使用全局變量
2.不要在線程之間共享GDI對象
3.讓主線程處理GUI界面
4.要等其他線程結束在結束程序
========
總結
以上是生活随笔為你收集整理的Win32 多线程学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 宏病毒学习总结
- 下一篇: Flex命令行学习总结