日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Win32 多线程学习总结

發布時間:2025/4/14 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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 多线程学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

中文字幕日韩有码 | 日韩女同一区二区三区在线观看 | 久久综合99| 九九九热 | 亚洲aaa毛片| 国产网站在线免费观看 | 日本不卡一区二区 | 久久福利综合 | 亚洲精品字幕在线 | 国产精品毛片一区视频播不卡 | 欧美视频不卡 | av先锋影音少妇 | 日韩精品中文字幕有码 | 精品国产一区二区在线 | 欧美久久综合 | 久久久久久久久精 | 久久久久久高潮国产精品视 | 亚洲精品在线视频观看 | www.五月天色 | 久久桃花网 | 精品国产大片 | 91福利影院在线观看 | 国产精品久久久亚洲 | 热re99久久精品国产99热 | 伊甸园永久入口www 99热 精品在线 | 在线观看a视频 | 最近中文字幕mv免费高清在线 | av免费在线看网站 | 精品视频网站 | 五月天中文字幕mv在线 | 在线亚洲播放 | 午夜精品成人一区二区三区 | 成人免费在线看片 | 美女网站色在线观看 | 国产精品麻豆视频 | 最近中文字幕大全 | 国产剧情av在线播放 | 天天操天天操天天操天天 | 伊人中文在线 | 国产精品成人一区二区三区吃奶 | 91精品国自产在线偷拍蜜桃 | av电影不卡在线 | 操久在线| 最近高清中文字幕 | 91中文字幕一区 | 久久综合之合合综合久久 | 午夜国产福利在线 | 九九久久久久99精品 | 日韩肉感妇bbwbbwbbw | 欧美激情综合色 | 国产一级黄 | 在线观看一级视频 | 最近在线中文字幕 | 天天综合亚洲 | 日韩视频一二三区 | 国产成人福利在线 | 在线观看香蕉视频 | 亚洲一级片| 黄色av免费 | 亚洲精品99久久久久中文字幕 | 日韩视频在线不卡 | 人人草在线视频 | 亚洲精品自拍 | 九九精品在线观看 | 久久黄色精品视频 | 粉嫩高清一区二区三区 | 中文字幕一区二区三区四区在线视频 | 国产美女精品视频 | 免费黄色a网站 | 成片视频免费观看 | 91免费黄视频 | 久久毛片网站 | 麻豆精品国产传媒 | 黄色在线观看污 | 欧美一级激情 | 美女久久久久久 | 久草免费色站 | 久久成人国产精品免费软件 | 欧美极品一区二区三区 | 在线欧美日韩 | 久草在 | 欧美日韩中文在线观看 | 亚洲高清视频在线观看免费 | 久久人人爽av | 91男人影院 | 国产精品美女久久久网av | 日日夜夜精品网站 | 99视频在线观看视频 | 在线观看黄网 | 国产精品黄色 | 国产乱对白刺激视频不卡 | 99久久久久国产精品免费 | 激情小说网站亚洲综合网 | 久久av免费 | 狠狠干网址 | 久久人人爽av | 中文字幕av日韩 | 在线观看精品国产 | 天堂av在线 | 日韩色中色 | 国产精品美女久久久久久久 | 日韩com| 亚洲视频,欧洲视频 | 99久久精品免费看国产免费软件 | 久久久久久伊人 | 亚洲精品www久久久久久 | 日韩伦理片hd | 国产精品 欧美 日韩 | www国产亚洲| 六月丁香激情综合色啪小说 | 中文字幕一区二区三 | www.狠狠色.com| 99久久99久国产黄毛片 | 久久99视频免费 | 日韩在线观看三区 | 最新久久久 | 夜夜天天干 | 五月天电影免费在线观看一区 | 免费在线成人av电影 | 最近能播放的中文字幕 | 色就是色综合 | 久热只有精品 | 麻豆视频免费在线播放 | 亚洲黄色免费网站 | 欧美一级电影在线观看 | 欧美看片 | 国产成人精品一区二 | 欧美伦理一区二区 | 97碰碰精品嫩模在线播放 | 亚洲国产精品99久久久久久久久 | 欧美精品少妇xxxxx喷水 | 久久久国产电影 | av国产在线观看 | 国产精品久久久久久久久久 | 少妇性bbb搡bbb爽爽爽欧美 | 人人插人人 | 久草在线免费在线观看 | 黄色在线视频网址 | 天天干天天上 | 久久久久观看 | 日韩免费在线观看网站 | 亚洲 欧美 日韩 综合 | 国产精品久久久久久久婷婷 | 九九九热精品免费视频观看 | 91久久电影| 国产资源在线免费观看 | 国产精品18毛片一区二区 | 亚洲黄色三级 | 日日爽天天操 | 色视频在线免费观看 | 九九热只有这里有精品 | 日韩草比| 激情开心站 | 91 在线视频播放 | 亚洲精品在线观看视频 | 九九在线精品视频 | 亚洲精品国产成人 | 国产在线播放不卡 | 国产精品手机在线观看 | 日韩成人免费在线 | 久久久久久久久毛片精品 | 色网站中文字幕 | 在线观看免费视频 | 亚洲国产精品激情在线观看 | 亚洲春色奇米影视 | 九九精品久久 | 81精品国产乱码久久久久久 | 婷婷精品进入 | 亚洲午夜精品一区二区三区电影院 | 日韩videos | 日韩欧美高清不卡 | 久久久久久久免费 | 久草视频国产 | 国产精选在线 | 久久久久亚洲精品国产 | 亚洲视频 视频在线 | 久草在线免费电影 | 六月丁香综合 | 最近高清中文字幕在线国语5 | 在线视频日韩一区 | 日韩精品中文字幕av | 亚洲精品视频国产 | 中文字幕丝袜 | 五月婷婷六月综合 | 国产精品入口a级 | 久久婷婷视频 | 中文字幕视频一区二区 | 伊人精品在线 | 亚洲精品videossex少妇 | 丁香六月中文字幕 | 在线观看的av | 久久亚洲二区 | 国产专区一 | 韩日电影在线免费看 | 91麻豆精品91久久久久同性 | 欧美日bb | 免费国产在线观看 | 日韩久久精品一区二区三区 | 粉嫩一区二区三区粉嫩91 | 97麻豆视频 | 人人插人人费 | 热九九精品 | 91亚洲免费 | 国产精品视频999 | 91av原创| 在线免费国产视频 | 天天操天天射天天爱 | 91自拍视频在线 | 四虎影院在线观看av | 免费在线国产 | 日批视频在线播放 | 4438全国亚洲精品在线观看视频 | 三上悠亚一区二区在线观看 | 久久精品视频3 | 成人av电影免费在线播放 | 激情欧美xxxx | 98福利在线 | 精品在线免费视频 | 色婷婷久久一区二区 | 欧美日韩精品区 | 精品国产伦一区二区三区观看体验 | 亚洲成人黄色在线观看 | 久久黄网站 | 午夜视频在线观看一区二区三区 | 美女在线观看av | 国产午夜精品在线 | 麻豆视频网址 | 久久久视频在线 | 欧美在线视频a | 亚洲成a人片在线观看网站口工 | 日韩一区二区三免费高清在线观看 | 九九欧美视频 | av色网站| 日本护士三级少妇三级999 | 美女免费视频网站 | 久久er99热精品一区二区 | 免费一级日韩欧美性大片 | 色狠狠综合天天综合综合 | 射射色 | 超碰在线cao| 久久av中文字幕片 | 五月婷丁香网 | 一区二区三区手机在线观看 | 久久夜av| 在线黄色国产电影 | 国产一级精品在线观看 | 国产精品一区专区欧美日韩 | 五月婷丁香| 国产一区二区三区四区在线 | 在线成人一区 | 伊人五月天 | 91亚·色| 婷婷精品国产欧美精品亚洲人人爽 | 99久久精| 天天天色综合 | 日韩三区在线观看 | 国产中文字幕在线看 | 久久手机免费视频 | 韩国av在线 | 99热最新精品 | 婷婷综合久久 | 色999视频| 岛国精品一区二区 | 天天干天天操天天爱 | 香蕉影院在线播放 | 色偷偷网站视频 | 99精品国产在热久久 | 久久免费在线视频 | 久久一区二区三区超碰国产精品 | 在线观看av小说 | 中文字幕av在线不卡 | 国产欧美精品一区aⅴ影院 99视频国产精品免费观看 | 亚洲国产中文在线 | 91在线播放视频 | 91国内产香蕉 | 91亚洲永久精品 | 最新国产精品亚洲 | 欧美一区二区三区在线 | 久久精品亚洲综合专区 | 狠狠狠狠狠狠天天爱 | 国产色在线,com | 亚洲女人av | 国产午夜在线 | 国产精品成人av电影 | 久久久99国产精品免费 | 久久综合狠狠狠色97 | 中文字幕精品视频 | 特级黄录像视频 | 亚洲免费不卡 | 国产在线精品视频 | 在线观看av网 | 亚洲在线激情 | 中文字幕在线观看视频一区二区三区 | 欧美中文字幕第一页 | 丁香婷婷综合色啪 | 色伊人网| av中文字幕在线播放 | 成人久久18免费网站图片 | 国产精品12345| 韩国三级一区 | 日韩深夜在线观看 | 久久精品国产成人精品 | 精品专区一区二区 | 日韩在线观看网址 | 美女又爽又黄 | 国产精品久久久久久久久久尿 | 91精品视频播放 | 久久这里只精品 | 久久99精品国产一区二区三区 | 国产精品久久久久av免费 | 在线观看成人毛片 | www.五月婷婷| 国产亲近乱来精品 | 91porny九色在线播放 | 婷婷 综合 色 | 久久久久久久久久网 | 久色伊人| www.888av| 欧美另类高清 | 在线成人免费电影 | 国产色啪 | 免费看91的网站 | 亚洲精品高清一区二区三区四区 | 亚洲欧美国产精品18p | 黄网站app在线观看免费视频 | 日韩精品中文字幕一区二区 | 人人爽人人干 | 最近最新最好看中文视频 | 欧美视频日韩 | 97在线免费| 六月丁香激情综合色啪小说 | 亚洲精品视频在线免费播放 | 国产精品99久久久久久小说 | 日韩视频一区二区在线观看 | 久久国产精品影视 | 久久草网站 | 国产精品久久久久久电影 | 四虎永久免费网站 | 国产又粗又猛又黄又爽 | 国产精品福利午夜在线观看 | 日韩一二区在线 | 免费视频91 | 久久久69| 在线免费日韩 | 久久免费资源 | 激情久久久久 | 四虎在线影视 | 综合在线观看色 | 五月婷婷激情综合 | 爱色婷婷 | 99精品在线观看视频 | 操操操日日日干干干 | 国产精品日韩久久久久 | 欧美精品一二三 | 久久人人爽爽人人爽人人片av | 亚洲一区二区高潮无套美女 | 99精品免费久久久久久日本 | 九九免费观看视频 | 久久国产精品一二三区 | 国产剧情一区二区 | 久热只有精品 | 欧美亚洲另类在线视频 | 国产99自拍| 二区三区在线 | 国产视频97| 中文伊人 | 天天综合网久久 | 色综合婷婷久久 | 在线国产一区 | 91精品专区| 激情五月看片 | 日本女人的性生活视频 | 久久一区国产 | 亚洲天天综合网 | 欧美日韩在线电影 | 欧美国产日韩一区二区 | 国产精品国产三级国产不产一地 | 日韩欧美在线一区 | 中文字幕在线视频一区二区三区 | 国产v在线播放 | 精品女同一区二区三区在线观看 | 97超碰国产精品女人人人爽 | 五月婷婷色播 | 91精品视频在线免费观看 | 久久综合中文色婷婷 | 国内视频一区二区 | 91麻豆免费看 | 日本久久久久久久久 | 视频国产| 干av在线 | 国产精品中文字幕在线播放 | 久久天天躁狠狠躁亚洲综合公司 | 成人永久在线 | bbb搡bbb爽爽爽 | 在线观看www91| 久久精品欧美日韩精品 | 99热只有精品在线观看 | 日本精品久久久一区二区三区 | 亚洲国产精品va在线看黑人 | 黄色网中文字幕 | 中文字幕在线观看第三页 | 午夜av影院 | 久久久国产毛片 | 日韩免费在线视频 | 国产美女视频一区 | av免费看在线 | 成人av电影免费在线播放 | 久久午夜精品影院一区 | 国产精品 日韩 | 中国一区二区视频 | 国产精品99久久久久久人免费 | 狠狠色丁香婷婷综合久小说久 | 亚洲激情在线观看 | 日韩黄色在线 | www免费看片com| 亚洲精品在线视频播放 | 国产美腿白丝袜足在线av | 日韩啪啪小视频 | av一二三区 | 国产高清视频免费在线观看 | av免费看电影 | 18久久久久 | 色婷婷丁香| 日日操夜 | 国产精品普通话 | 日韩精品五月天 | 最新超碰在线 | 天堂av在线中文在线 | 黄色毛片视频免费观看中文 | av官网在线 | 久久影院中文字幕 | 热久久免费国产视频 | 亚洲精品国久久99热 | 欧美日韩免费一区二区 | 国产91精品高清一区二区三区 | 成人一级在线观看 | 成片视频在线观看 | 美女av免费看| 亚洲最新av | www国产亚洲精品久久麻豆 | 久草在线视频免赞 | 美女视频免费精品 | 一区二区成人国产精品 | 久久久一本精品99久久精品 | 夜色资源站国产www在线视频 | 欧美精品一区二区免费 | 日韩小视频 | 久草视频播放 | 日韩免费b| 日日爱网址 | 久草在线久草在线2 | 99麻豆视频 | 中文在线√天堂 | 久久久www成人免费精品张筱雨 | 中文在线最新版天堂 | 亚洲资源视频 | 福利视频一区二区 | 91高清免费观看 | 国产精品久久久一区二区三区网站 | 天天插天天操天天干 | 日p在线观看 | 激情在线网站 | 182午夜在线观看 | 国产精品18久久久久久首页狼 | av网站地址 | 91污污视频在线观看 | 亚洲精品1区2区3区 超碰成人网 | 欧美综合久久 | av解说在线 | 午夜av色 | 美女免费视频观看网站 | 久久无码av一区二区三区电影网 | 亚洲乱码久久 | 日韩精品一区不卡 | 一级片免费视频 | av+在线播放在线播放 | 天天射天天舔天天干 | 国产精品免费一区二区三区 | 欧美日韩久久不卡 | 国产最新精品视频 | 国产精品久久视频 | 黄色1级大片 | 天堂在线v | 成人毛片100免费观看 | 伊人黄色网 | 亚洲欧美综合精品久久成人 | 一区二区精品久久 | 青春草视频在线播放 | 久久久www | 香蕉视频一级 | 国产在线不卡 | 久久免费99 | 在线观看成人毛片 | 日日夜夜操操操操 | 欧美成人性战久久 | 99在线精品视频观看 | 成人黄色片免费看 | 日韩三级在线观看 | 高清av影院| 97香蕉超级碰碰久久免费软件 | 亚洲精品女 | 中文字幕 国产视频 | 美女国产 | 黄色大片视频网站 | 中文字幕色网站 | 国产高清精品在线 | 91免费视频国产 | 天天操天天玩 | 探花视频在线观看+在线播放 | 国产精品免费不卡 | 日韩一区正在播放 | 免费久久网 | 香蕉视频在线视频 | 久久久视频在线 | 中文字幕欧美激情 | 91麻豆视频 | 精品国产精品久久一区免费式 | av亚洲产国偷v产偷v自拍小说 | 免费黄色网址大全 | 午夜色站| 国产精品久久久久久久毛片 | 一区二区视频免费在线观看 | 人人爽人人爽人人 | www国产亚洲精品久久网站 | 精品久久毛片 | 天天草夜夜 | av三级在线播放 | 亚洲精品美女久久久久网站 | 精品国产一区二区三区久久久久久 | 日韩视频一区二区在线观看 | 国产超碰97| 欧美高清成人 | 国产91在线播放 | 成人丝袜 | av在线免费不卡 | 日韩艹 | 日韩欧美大片免费观看 | 在线观看av麻豆 | 国产日韩在线视频 | 91精品第一页 | 黄色在线免费观看网址 | 午夜精品一区二区三区免费 | 亚洲免费不卡 | 久久精品第一页 | 日本黄色大片免费看 | 精品一区二区免费视频 | 十八岁免进欧美 | 国产精品免费视频久久久 | 中文字幕精品三级久久久 | 国产一二区视频 | 久久毛片网站 | 日韩久久久久久久久 | 午夜精品成人一区二区三区 | 夜夜操夜夜干 | 四虎国产精品免费观看视频优播 | 久久免费福利 | 久久最新视频 | 久草视频在线免费播放 | 69性欧美 | 久久久久久久久久久久久国产精品 | 超碰人人草人人 | 国产在线成人 | 国产成人精品久久久久 | 免费网站色 | 在线不卡的av | 爱av在线网 | 日韩黄色在线电影 | 91精品秘密在线观看 | 亚洲2019精品 | av一区二区在线观看中文字幕 | 日韩欧美国产成人 | 欧美特一级 | 国产福利在线免费观看 | 国产免费精彩视频 | 456成人精品影院 | 99热在线免费观看 | 久草视频资源 | 91视频大全| 亚洲精品小视频在线观看 | 丰满少妇对白在线偷拍 | 在线亚洲观看 | 夜夜操天天 | 中文字幕成人在线观看 | 日韩理论在线 | 六月丁香婷婷网 | 在线视频日韩一区 | 丁香激情五月 | 亚洲影院一区 | 国产精品久久久久久久久久久久 | 最新日韩在线 | 日本天天色| 色婷婷综合久久久久中文字幕1 | 视频在线日韩 | 成人av教育 | 精品视频中文字幕 | 一区二区三区在线电影 | 国产又粗又猛又色 | 亚洲最大av在线播放 | 国产精品av在线免费观看 | 亚洲天天摸日日摸天天欢 | 久久婷综合 | 久久久久电影 | 亚洲国产高清在线 | 中文字幕xxxx | 久草国产视频 | 一区二区三区在线免费 | 成人小电影在线看 | 黄色网在线免费观看 | 丁香花在线观看免费完整版视频 | 日韩精品一区电影 | 亚洲成年人免费网站 | 婷婷综合导航 | 毛片美女网站 | 久久只精品99品免费久23小说 | 亚洲精品男女 | 香蕉精品视频在线观看 | 超级碰碰免费视频 | 久久久久久久久久久久电影 | 亚洲国产成人久久综合 | 亚洲国产经典视频 | 一级特黄av| 成人在线播放免费观看 | 色网址99 | 日本中文一区二区 | 操处女逼 | 最新精品国产 | 欧美精品久久久久久久久久久 | 在线看一级片 | 极品美女被弄高潮视频网站 | 国产一级二级视频 | 黄色a大片 | 五月婷婷中文字幕 | 婷婷在线免费 | 国产精品国产三级国产不产一地 | 国产精品18久久久久久久网站 | 成年人三级网站 | 久久99热久久99精品 | 久久人人干 | 亚洲码国产日韩欧美高潮在线播放 | 国产精品igao视频网入口 | 色综合天天视频在线观看 | 欧美视频一区二 | 久久久久久久久久久久国产精品 | 就要色综合 | 日韩综合一区二区 | 亚洲va欧美 | 免费看国产一级片 | 亚洲黄色免费在线 | 欧美精品黑人性xxxx | 五月花丁香婷婷 | 最新高清无码专区 | 日本激情动作片免费看 | 婷婷在线资源 | 五月天综合色 | 免费在线成人 | 日韩天堂在线观看 | 一区二区三区免费在线播放 | 在线视频亚洲 | 久艹视频免费观看 | 天天综合视频在线观看 | 97成人精品视频在线播放 | 91免费观看视频网站 | 99免费| 97成人精品区在线播放 | 国产免费av一区二区三区 | 精品一区二区三区四区在线 | 丁香九月婷婷 | 91丨精品丨蝌蚪丨白丝jk | 97超碰人人网 | 蜜臀av性久久久久av蜜臀三区 | 亚洲欧美国产精品久久久久 | 国产精品99久久久精品 | 国产精品久久久久久影院 | 91久久久久久国产精品 | 麻花豆传媒mv在线观看 | 国产在线污 | 免费在线观看av | 欧美日韩国内在线 | 亚洲综合五月天 | 五月天九九 | 最近中文国产在线视频 | www99久久| 97涩涩视频 | 色视频网站在线观看一=区 a视频免费在线观看 | 日本久久久久久 | 国产成人高清av | 最新av免费在线 | 国产精品久久久av | 国产香蕉97碰碰碰视频在线观看 | 亚州免费视频 | 麻豆影音先锋 | 日本激情视频中文字幕 | 日韩特级黄色片 | 日韩精品一区二区三区水蜜桃 | 美女精品 | 亚洲 在线 | 国产亚洲精品久久久久久无几年桃 | 国产精品久久久久久久久久久久 | 国产精品久久久久久欧美 | 在线视频1卡二卡三卡 | 国产成人精品三级 | 色欧美88888久久久久久影院 | 国产精品久久久久久久妇 | 日韩av看片 | 日本黄色一级电影 | 精品亚洲男同gayvideo网站 | 成人免费观看在线视频 | 天天操天天色天天 | 奇米影视999 | 一区二区 不卡 | 色婷婷福利视频 | 久久精品久久精品久久精品 | 天堂v中文 | 成年人在线免费看视频 | 一区二区三区精品在线视频 | 成人性生交大片免费观看网站 | 久久99精品一区二区三区三区 | 久操伊人 | 在线播放日韩av | 亚洲成人av影片 | 中文字幕专区高清在线观看 | 国产日韩精品欧美 | 欧美一级特黄高清视频 | 国模视频一区二区 | 在线性视频日韩欧美 | 久久久久国产一区二区三区四区 | 中文字幕在线观看视频一区 | 色综合小说 | 九草在线视频 | 久久成| 天天综合精品 | 国产伦精品一区二区三区在线 | 日韩动漫免费观看高清完整版在线观看 | 午夜电影中文字幕 | 亚洲视频综合在线 | 91香蕉国产在线观看软件 | 天天爱天天操 | 中文国产在线观看 | 91九色丨porny丨丰满6 | 成年人国产在线观看 | 国产在线综合视频 | 少妇性aaaaaaaaa视频 | 在线直播av | 六月天综合网 | 成人精品国产免费网站 | 射射射av| 99热99热| 久久一区二区三区国产精品 | 97视频免费在线看 | 日韩欧美电影网 | 高清中文字幕av | 激情视频网页 | 狠狠狠操 | www.人人草| 色视频在线免费观看 | 婷婷亚洲综合 | 久久噜噜少妇网站 | 国产一性一爱一乱一交 | 婷婷精品国产欧美精品亚洲人人爽 | 在线色吧 | 人人干狠狠操 | 亚洲精品成人av在线 | 五月亚洲综合 | 国产成人一区二区三区免费看 | 九九视频精品免费 | 天天操夜| 制服丝袜在线 | 黄色av免费看| 麻豆视频一区 | 亚洲综合在线一区二区三区 | 国产精品免费在线播放 | 国产又粗又猛又爽又黄的视频先 | 又黄又刺激 | 国产亚洲精品久久久久5区 成人h电影在线观看 | 亚洲最大av在线播放 | 毛片永久免费 | 亚洲激情在线播放 | 丝袜制服综合网 | 免费看污片| 婷婷色五 | 久久久精品一区二区 | 久久视了| 久久久国产在线视频 | 国产综合精品久久 | 久久婷婷丁香 | 日韩av免费大片 | 91视频免费看 | 中文字幕在线看视频国产 | 国产高清在线观看 | 9ⅰ精品久久久久久久久中文字幕 | 国产成人免费在线 | 看v片| 亚洲一区二区三区四区在线视频 | 日韩在线第一 | 欧美精品久久久久a | 亚洲激情五月 | 黄色在线观看网站 | 色综合天天狠天天透天天伊人 | 激情久久伊人 | 天堂中文在线播放 | 最新av在线免费观看 | 亚洲精品白浆高清久久久久久 | 精品99在线 | 天堂av在线网 | 国产免费不卡 | 亚洲视频在线观看免费 | 久久大片网站 | 国产视频久久 | 一区二区三区四区精品 | 国产精品日韩在线观看 | 精品久久国产 | 操操日 | 久久视频二区 | 在线电影 一区 | 亚洲综合在线视频 | 精品91视频 | 日韩激情综合 | 97在线免费观看视频 | 亚洲精品在线观 | 91成人免费在线视频 | 91原创在线观看 | 亚洲日本中文字幕在线观看 | 在线色亚洲 | 日本中文字幕在线看 | 亚洲精品综合欧美二区变态 | 91 | 日韩成人一级大片 | 成人精品国产 | 久久精品国产精品亚洲精品 | av在线com| 亚洲午夜在线视频 | 一区二区三区免费在线观看视频 | 国产精品国产三级国产不产一地 | 美女一区网站 | 亚洲免费a | 中文字幕中文字幕在线中文字幕三区 | 欧美性生活小视频 | 丁香在线观看完整电影视频 | 黄色a视频免费 | 草久在线观看视频 | 国产夫妻性生活自拍 | 欧美孕妇与黑人孕交 | 精品国模一区二区 | 国产高清网站 | 人人插人人搞 | 色婷婷色 | 91视频链接| 国产精品国产亚洲精品看不卡15 | 伊在线视频 | 成人在线观看免费视频 | 一区二区三区三区在线 | 免费高清男女打扑克视频 | 7777精品伊人久久久大香线蕉 | 国产麻豆果冻传媒在线观看 | 伊人资源视频在线 | 91亚色视频 | 国产裸体视频网站 | 久久久久欧美精品999 | 欧美日韩免费观看一区=区三区 | 精品二区视频 | 精品福利视频在线观看 | 中文字幕亚洲欧美日韩 | 日本公妇在线观看高清 | 96久久精品 | 国产精品欧美久久久久三级 | 中文字幕大全 | 午夜色性片 | 丁香视频五月 | 亚洲粉嫩av | 国产特级毛片aaaaaaa高清 | 激情综合电影网 | 亚洲五月激情 | 久久视频在线观看中文字幕 | 亚洲婷婷综合色高清在线 | 日韩在线视频一区二区三区 | 欧美激情一区不卡 | 日韩av一区二区在线播放 | 国产在线观| 精品免费视频123区 午夜久久成人 | 天天射夜夜爽 | 成人理论在线观看 | 久草在线视频免费资源观看 | 亚洲激情在线观看 | 色av男人的天堂免费在线 | 国产精品99久久久久 | 国产亚洲精品精品精品 | 日韩国产精品一区 | 不卡视频一区二区三区 | 国产精品日韩在线观看 | 日韩色区 | 国产精品ssss在线亚洲 | 中文字幕免费久久 | 久久精品人 | 久久99热这里只有精品国产 | 超碰在线日本 | 久草免费在线视频观看 | 亚洲精品视频第一页 | 99精品视频免费观看视频 | 国产成人l区 | 奇米影视8888在线观看大全免费 | 成人小视频在线观看免费 | 亚洲日本在线一区 | 国产 日韩 中文字幕 | 最近最新中文字幕 | 成人中文字幕+乱码+中文字幕 | 欧美在线99 | 狠狠精品 | 久久视频在线视频 | 色婷婷导航 | 欧美淫aaa免费观看 日韩激情免费视频 | 黄色www | 亚洲爱视频| 啪啪激情网 | 欧美激情第一页xxx 午夜性福利 | 亚洲欧美国产视频 | 国产h在线播放 | 中文字幕在线播放av | 精品免费视频123区 午夜久久成人 | 免费色视频在线 | 1000部国产精品成人观看 | 国产一级电影在线 | www.夜夜夜| 国产一级片不卡 | 五月婷婷丁香网 | 91福利社区在线观看 | 四虎8848免费高清在线观看 | 丰满少妇在线观看网站 | 亚洲精品高清在线 | 99久久精品国产欧美主题曲 | 成人av一区二区三区 | 久久精品一区二区三区国产主播 | 在线成人性视频 | 色婷婷久久久 | 精品国产乱码久久久久久1区2匹 | 国产欧美久久久精品影院 | 色九九在线 | 韩日av一区二区 | 有没有在线观看av | 国产成人三级三级三级97 | 欧美精品xxx| 久久观看最新视频 | 日韩簧片在线观看 | 有码视频在线观看 | 久久96国产精品久久99软件 | 香蕉网在线播放 | 天天玩夜夜操 | 精品一区二区久久久久久久网站 | 精品专区一区二区 | 免费精品视频 | 久久国产色 | 国产精品一区二区在线免费观看 | 久久精品9 | av不卡免费在线观看 | 亚洲天堂网在线视频 | 亚洲四虎在线 | 天天射天天射天天 | 天堂av色婷婷一区二区三区 | 91在线中文字幕 | 中文在线天堂资源 | 久久公开视频 | 在线看国产 | 久在线 | 一二三久久久 | 国产黄色在线看 | 精品国产区在线 | 在线一级片 | 国产精品99视频 | 丁香激情五月婷婷 | 日日干 天天干 | 99久久精品日本一区二区免费 | 97久久久免费福利网址 | 天天爽天天搞 | 成人免费视频网 | 日本久久久久久久久久久 | 大型av综合网站 | 亚洲我射av | 日韩免费视频观看 | 一区二区精品在线 | 色网av| 日韩免费专区 | 免费精品视频在线 | 亚洲精品h | 久久久久久久久久网站 | 成片免费观看视频 | 精品理论片 | 91九色成人 | 波多野结衣一区三区 | 亚洲天堂精品 | 色天天久久 | 亚洲mv大片欧洲mv大片免费 | av福利在线看 | 91人网站| 97av超碰| 久久免费看av | 国产在线免费 | 涩涩网站在线播放 | 日日操狠狠干 | 日韩精品第1页 |