青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

大龍的博客

常用鏈接

統(tǒng)計

最新評論

線程函數(shù)的設計以及MsgWaitForMultipleObjects函數(shù)的使用要點 ----- 轉

使用多線程技術可以顯著地提高程序性能,本文就講講在程序中如何使用工作線程,以及工作線程與主線程通訊的問題。

 

創(chuàng)建線程

 

       使用MFC提供的全局函數(shù)AfxBeginThread()即可創(chuàng)建一個工作線程。線程函數(shù)的標準形式為 UINT MyFunProc(LPVOID );此函數(shù)既可以是全局函數(shù),也可以是類的靜態(tài)成員函數(shù)。之所以必須是靜態(tài)成員函數(shù),是由于類的非靜態(tài)成員函數(shù),編譯器在編譯時會自動加上一個this指針參數(shù),如果將函數(shù)設置為靜態(tài)的成員函數(shù),則可以消除this指針參數(shù)。如果想在線程函數(shù)中任意調(diào)用類的成員變量(此處指的是數(shù)據(jù)成員,而不是控件關聯(lián)的成員變量),則可以將類的指針作為參數(shù)傳遞給線程函數(shù),然后經(jīng)由該指針,就可以調(diào)用類的成員變量了。

//線程函數(shù),類的靜態(tài)成員函數(shù)

UINT CThreadTest::TH_SetProgress(LPVOID lpVoid)

{

       CThreadTest *pTest=(CThreadTest *)lpVoid;

       pTest->SetProgress();

       return 0;

}

//類的成員函數(shù),此函數(shù)執(zhí)行實際的線程函數(shù)操作,卻可以自如的調(diào)用成員數(shù)據(jù)

void CThreadTest::SetProgress()

{

int nCount=0;

       while (1)

       {

              m_progress.SetPos(nCount); //設置進度條進度

//            this->SendMessage(WM_SETPROGRESSPOS,nCount,0);//也可以采用這種方式設置

              nCount++;

              if (g_exitThread)

              {

                     return;

              }

              Sleep(200);

       }

}

 

線程函數(shù)體的設計

 

有過多線程設計經(jīng)驗的人都有體會,多線程設計最重要的就是要處理好線程間的同步和通訊問題。如解決不好這個問題,會給程序帶來潛藏的隱患。線程的同步可以利用臨界區(qū)、事件、互斥體和信號量來實現(xiàn),線程間的通訊可利用全局變量和發(fā)消息的形式實現(xiàn)。其中事件和臨界區(qū)是使用得比較多的工具。請看下面的線程函數(shù)體:

UINT AnalyseProc(LPVOID   lVOID)

{

       if(WAIT_OBJECT_0== WaitForSingleObject(m_eventStartAnalyse.m_hThread,INFINITE))

       {

              while (WAIT_OBJECT_0 == WaitForSingleObject(m_eventExitAnalyse.m_hThread,0))

              {

                     DWORD dRet=WaitForSingleObject(m_eventPause.m_hThread,0);

                     if (dRet == WAIT_OBJECT_0)

                     {

                            //暫停分析

                            Sleep(10);

                     }

                     else if (dRet == WAIT_TIMEOUT)

                     {

                            //繼續(xù)分析

                            //

                     }

              }

       }

 

       return 0;

}

 

上面的線程函數(shù)用到了三個事件變量eventStartAnalyseeventExitAnalyseeventPause,分別用來控制線程函數(shù)的啟動、退出以及暫停。再配以WaitForSingleObject函數(shù),就可以自如的控制線程函數(shù)的執(zhí)行,這是在線程函數(shù)體內(nèi)應用事件變量的典型方式,也是推薦的方式。

無論是工作線程還是用戶界面線程,都有消息隊列,都可以接收別的線程發(fā)過來的消息也可以給別的線程發(fā)送消息。給工作線程發(fā)消息使用的函數(shù)是PostThreadMessage()。此函數(shù)的第一個參數(shù)是接收消息的線程的ID。此函數(shù)是異步執(zhí)行的,機制和PostMessage一樣,就是把消息拋出后就立即返回,不理會消息是否被處理完了。

這里還有著重強調(diào)一點,線程消息隊列是操作系統(tǒng)幫我們維護的一種資源,所以它的容量也是有限制的。筆者曾經(jīng)做過實驗,在5~6秒事件內(nèi)調(diào)用PostThreadMessage往線程消息隊列里發(fā)送5萬多條消息,可是由于線程函數(shù)處理消息的速度遠慢于發(fā)送速度,結果導致線程消息隊列里已經(jīng)堆滿了消息,而發(fā)送端還在發(fā)消息,最終導致消息隊列溢出,很多消息都丟失了。所以,如果你要在短時間內(nèi)往線程消息隊列里發(fā)送很多條消息,那就要判斷一下PostThreadMessage函數(shù)的返回值。當消息隊列已經(jīng)溢出時,此函數(shù)返回一個錯誤值。根據(jù)返回值,你就可以控制是否繼續(xù)發(fā)送。

工作線程給主線程發(fā)消息使用的是SendMessagePoseMessage函數(shù)。這兩個函數(shù)的區(qū)別在于SendMessage函數(shù)是阻塞方式,而PoseMessage函數(shù)是非阻塞方式。如果不是嚴格要求工作線程與主線程必須同步執(zhí)行,則推薦使用PoseMessage。不要在線程函數(shù)體內(nèi)操作MFC控件,因為每個線程都有自己的線程模塊狀態(tài)映射表,在一個線程中操作另一個線程中創(chuàng)建的MFC對象,會帶來意想不到的問題。更不要在線程函數(shù)里,直接調(diào)用UpdataData()函數(shù)更新用戶界面,這會導致程序直接crash。而應該通過發(fā)送消息給主線程的方式,在主線程的消息響應函數(shù)里操作控件。上面提到的SetProgress函數(shù)和AnalyseProc函數(shù)均為線程函數(shù),但它們都不能接收別的線程發(fā)過來的消息,雖然它們都可以給主線程發(fā)消息。它們要想能夠接收別的線程發(fā)過來的消息,則必須調(diào)用GetMessagePeekMessage函數(shù)。這兩個函數(shù)的主要區(qū)別在于:

GetMessage函數(shù)可以從消息隊列中抓取消息,當抓取到消息后,GetMessage函數(shù)會將此條消息從消息隊列中刪除。而且,如果消息隊列中沒有消息,則GetMessage函數(shù)不會返回,CPU轉而回去執(zhí)行別的線程,釋放控制權。GetMessage返回的條件是抓取的消息是WM_QUIT

PeekMessage函數(shù)也可以從消息隊列中抓取消息,如果它的最后一個參數(shù)設置為PM_NOREMOVE,則不從消息隊列中刪除此條消息,此條消息會一直保留在消息隊列中。如果它的最后一個參數(shù)是PM_REMOVE,則會刪除此條消息。如果消息隊列中沒有消息,則PeekMessage函數(shù)會立刻返回,而不是像GetMessage一樣就那樣等在那兒。PeekMessage函數(shù)就像是窺探一下消息隊列,看看有沒有消息,有的話就處理,沒有就離開了。這一點也是兩個函數(shù)的最大不同。下面的代碼演示了在線程函數(shù)中使用這兩個函數(shù)的三種方式,這三種方法可以達到同樣的效果:

void CThreadTest::SetSlider()

{

 

// 在線程函數(shù)里啟動一個時鐘,每50毫秒發(fā)送一個WM_TIMER消息

       int nTimerID=::SetTimer(NULL,1,50,NULL);

 

       int nSliderPos=0;

 

       MSG msg;

       while (1)

       {

//方式一    使用GetMessage函數(shù)  

/*           if (::GetMessage(&msg,NULL,0,0))

              {

                     switch(msg.message)

                     {

                     case WM_TIMER:

                            {

                                   nSliderPos++;

                         ::SendMessage(this->m_hWnd,WM_SETSLIDERPOS,nSliderPos,0);

                            }                         

                            break;

                     case WM_QUIT_THREAD: //自定義消息

                            {

                                   ::KillTimer(NULL,1);

                                   return;

                            }                  

                         break;

                     default:

                         break;

                     }

              }    

 

 */

 

//方式二   使用PeekMessage函數(shù)  

 

/*           if (::PeekMessage(&msg,NULL,0,0,PM_REMOVE))

              {

                     switch(msg.message)

                     {

                     case WM_TIMER:

                            {

                                   nSliderPos++;

                                                     ::SendMessage(this->m_hWnd,WM_SETSLIDERPOS,nSliderPos,0);

                            }                         

                            break;

                     case WM_QUIT_THREAD: //自定義消息

                            {

                                   ::KillTimer(NULL,1);

                                   return;

                            }                  

                         break;

                     default:

                         break;

                     }

              }

              else

              {

                       //必須有此操作,要不然當沒有消息到來時,線程函數(shù)相當于陷

//入空循環(huán),cpu的占有率會飆升

                     Sleep(20);

              }

*/

 

//方式三   同時使用PeekMessageGetMessage函數(shù)  

 

              if (::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE))

              {

                     if(::GetMessage(&msg,NULL,0,0))

                     {

                            switch(msg.message)

                            {

                            case WM_TIMER:

                                   {

                                          nSliderPos++;                                                                    ::SendMessage(this->m_hWnd,WM_SETSLIDERPOS,nSliderPos,0);

                                   }                         

                                   break;

                            case WM_QUIT_THREAD: //自定義消息

                                   {

                                          ::KillTimer(NULL,1);

                                          return;

                                   }                  

                                   break;

                            default:

                                   break;

                            }

                     }

              }

              else

              {

                     Sleep(20);

              }

       }

}

前面已經(jīng)介紹過了,不建議線程函數(shù)里用SendMessage給主線程發(fā)消息,因為這個函數(shù)是同步操作,就是如果SendMessage函數(shù)不執(zhí)行完,是不會返回的,這樣線程函數(shù)就無法繼續(xù)執(zhí)行。有時這種操作容易導致工作線程和主線程死鎖,這個我們后面會談到,會介紹一種解決方法。

 

線程的退出

 

線程的退出有多種方式,比如可以調(diào)用TerminateThread()函數(shù)強制線程退出,但不推薦這種方式,因為這樣做會導致線程中的資源來不及釋放。最好的也是推薦的方式,是讓線程函數(shù)自己退出。就像上面介紹的SetProgress()函數(shù)中,用全局變量g_exitThread使線程退出。

AnalyseProcWAIT_OBJECT_0 ==WaitForSingleObject(m_eventExitAnalyse.m_hThread,0)這種方式來退出線程,還有在SetSlider函數(shù)中利用發(fā)送自定義消息WM_QUIT_THREAD的方式令線程退出。這些都是可以使用的方法。

       當主線程要退出時,為了能保證線程的資源能全部地釋放,主線程必須等待工作線程退出。線程對象和進程對象一樣,也是內(nèi)核對象,而且線程對象的特點是當線程退出時,線程內(nèi)核對象會自動變?yōu)橛行盘枲顟B(tài),能夠喚醒所有正在等待它的線程。我們通常都習慣于使用WaitForSingleObject等函數(shù)來等待某個內(nèi)核對象變?yōu)橛行盘枲顟B(tài),但是我想說的是,在主線程中不要使用WaitForSingleObjectWaitForMultipleObjects兩個函數(shù)等待線程退出,其原因就是有導致程序死鎖的隱患,特別是線程函數(shù)里調(diào)用了SendMessage或是直接操作了MFC對象,更易出現(xiàn)此種現(xiàn)象。下面的函數(shù)是一個在主線程中用來等待SetProgress()線程函數(shù)退出的函數(shù):

 

//退出線程

void CThreadTest::OnButton2()

{

       g_exitThread=TRUE; //設置全局變量為真,令線程退出

 

#if 1

 

       WaitForSingleObject(m_pThread1->m_hThread,INFINITE); //無限等待

 

#else

 

       DWORD dRet;

       MSG msg;

 

       while (1)

       {

              dRet=::MsgWaitForMultipleObjects(1,&m_pThread1->m_hThread,FALSE,INFINITE,QS_ALLINPUT);

 

              if (dRet == WAIT_OBJECT_0+1)

              {

                     while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))

                     {

                            TranslateMessage(&msg);

                            DispatchMessage(&msg);

                     }

              }

              else

              {

                     break;

              }

       }

      

#endif    

}

在上面的函數(shù)中我用#if #else #endif這組預編譯指令控制函數(shù)的執(zhí)行代碼,如果我令#if 1,則執(zhí)行WaitForSingleObject函數(shù),如果我令#if 0,則執(zhí)行DWORD dRet路徑。首先令#if  1,測試會發(fā)現(xiàn),程序死鎖了。原因是當程序執(zhí)行到WaitForSingleObject函數(shù)時,主線程掛起,等待線程函數(shù)退出,此時CPU切換到線程函數(shù)體內(nèi)執(zhí)行,如果執(zhí)行到if (g_exitThread)處,則線程函數(shù)順利退出,可如果執(zhí)行到m_progress.SetPos(nCount)處,由于SetPos函數(shù)是在主線程中完成的操作,Windows是基于消息的操作系統(tǒng),很多操作都是靠發(fā)消息完成的,由于主線程已經(jīng)掛起,所以沒有機會去消息隊列中抓取消息并處理它,結果導致SetPos函數(shù)不會返回,工作線程也被掛起,典型的死鎖。如果不用m_progress.SetPos,而改用this->SendMessage(…),其結果是一樣的。此時如果用了PostMessage,則工作線程會順利退出,因為PostMessage是異步執(zhí)行的。由此可見,在主線程中用WaitForSingleObject等待工作線程退出是有很大隱患的。

       為解決這一問題,微軟特提供了一個MsgWaitForMultipleObjects函數(shù),該函數(shù)的特點是它不但可以等待內(nèi)核對象,還可以等消息。也就是當有消息到來時,該函數(shù)也一樣可以返回,并處理消息,這樣就給了工作線程退出的機會。

DWORD MsgWaitForMultipleObjects(
DWORD nCount, //要等待的內(nèi)核對象數(shù)目
LPHANDLE pHandles, //要等待的內(nèi)核對象句柄數(shù)組指針
BOOL fWaitAll, //是等待全部對象還是單個對象
DWORD dwMilliseconds,//等待時間 
DWORD dwWakeMask );//等待的消息類型
 
下面就詳解一下該函數(shù)的參數(shù)使用方法:
DWORD nCount:要等待的內(nèi)核對象的數(shù)目。如果等待兩個線程退出,則nCount=2
LPHANDLE pHandles:要等待的內(nèi)核對象句柄數(shù)組指針。
 
如果只要等待一個線程退出,則直接設置該線程句柄的指針即可:
MsgWaitForMultipleObjects(1,&m_pThread->m_hThread,…)
 
如果要等待兩個線程退出,則使用方法為:
HANDLE hArray[2]={ m_pThread1->m_hThread , m_pThread2->m_hThread }
MsgWaitForMultipleObjects(2,hArray,…)
 
BOOL fWaitAllTRUE-表示只有要等待的線程全部退出后,此函數(shù)才返回,
               FALSE-表示要等待的線程中任意一個退出了,或是有消息到達了,此函數(shù)均會返回。
在上面的OnButton2()函數(shù)中,我要等待一個線程退出,將fWaitAll設置為
FALSE,目的是無論是線程真的退出了,還是有消息到達了,該函數(shù)都能返回。
如果將該fWaitAll設置為TRUE,那么函數(shù)返回的唯一條件是線程退出了,即便
是有消息到來了,該函數(shù)也一樣不會返回。
 
DWORD dwMilliseconds:等待的事件,單位是毫秒。可以設置為INFINITE,無
窮等待
 
DWORD dwWakeMask:等待的消息類型,通常可以設置為QS_ALLINPUT。此宏表示的是可以等待任意類型的消息。當然,也可以指定等待的消息類型。
 
#define QS_ALLINPUT        (QS_INPUT         | \
                            QS_POSTMESSAGE   | \
                            QS_TIMER         | \
                            QS_PAINT         | \
                            QS_HOTKEY        | \
                            QS_SENDMESSAGE)
 

返回值:DWORD dRet 通過函數(shù)返回值,可以得到一些有效信息。函數(shù)返回值依fWaitAll設置的不同而有所不同。下面是函數(shù)返回值的幾種常見類型:

dRet = 0xFFFFFFFF    表示函數(shù)調(diào)用失敗,可用GetLastError()得到具體的出錯信息;

dRet =WAIT_OBJECT_0+nCount:表示有消息到達了;

 

如果fWaitAll設置為TRUE

dRet = WAIT_OBJECT_0,表示所有等待的核心對象都激發(fā)了,或是線程都退出了;

如果fWaitAll設置為FALSE

dRet = WAIT_OBJECT_0 ~ WAIT_OBJECT_0+nCount-1:表示等待的內(nèi)核對象被激發(fā)了,index=dRet - WAIT_OBJECT_0,表示hArray[]數(shù)組中索引為index的那個對象被激發(fā)了。

 

當函數(shù)由于消息到來而返回,則需要用戶主動去消息隊列中將消息抓取出來,然后派發(fā)出去,這樣該消息就會被處理了。其具體的操作就是:

while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))

{

       TranslateMessage(&msg);

       DispatchMessage(&msg);

}

 

下面再看一個用這個函數(shù)等待兩個線程退出的例子:

//關閉線程12

void CThreadTest::OnButton6()

{

      

      

       DWORD dRet=-2;

       HANDLE hArray[2]; 

      

       hArray[0]=m_pThread1->m_hThread;

       hArray[1]=m_pThread2->m_hThread;

 

       MSG msg;

 

       int nExitThreadCount=0;       //標記已經(jīng)有幾個線程退出了

       BOOL bWaitAll=FALSE;

       int nWaitCount=2;    //初始等待的線程數(shù)目

 

       while (1)

       {

              dRet=MsgWaitForMultipleObjects(nWaitCount,hArray,bWaitAll,INFINITE,QS_ALLINPUT);

              if (dRet == WAIT_OBJECT_0+ nWaitCount)

              {

                     TRACE("收到消息,函數(shù)返回值為%d \n",dRet);

                     while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))

                     {

                            TranslateMessage(&msg);

                            DispatchMessage(&msg);

                     }

                    

              }

              else if (dRet >= WAIT_OBJECT_0 && dRet < WAIT_OBJECT_0+ nWaitCount)

              {

                     nExitThreadCount++;

                     if (nExitThreadCount == 1)

                     {

                            TRACE("一個線程退出了\n");

                            int nIndex=dRet-WAIT_OBJECT_0;

                            hArray[nIndex]=hArray[nWaitCount-1];

                            hArray[nWaitCount-1]=NULL;

                            nWaitCount--;

 

                     }

                     else

                     {

                            TRACE("兩個線程都退出了\n");

                            break;

                     }

              }

              else

              {

                     DWORD dErrCode=GetLastError();

                    

                     break;

              }

       }

      

}

 

在上面這個例子中,我將bWaitAll設置為FALSE,目的是當我要等待的兩個線程中由一個退出了,或是有消息到來了,此函數(shù)都可以退出。如果我將此參數(shù)設置為TRUE,那么,當且僅當我要等待的兩個線程均退出了,這個函數(shù)才會返回,這種使用方法有是程序陷入死鎖的危險,故應避免。無論是等待一個還是多個線程,只需將此參數(shù)設置為FALSE即可,然后通過函數(shù)返回值判斷究竟是那個返回了,還是消息到達了即可。這一要點前面已有陳述,此處再強調(diào)一遍。

通過函數(shù)返回值可以得知究竟哪個線程退出了,當要等待的兩個線程中的一個已經(jīng)退出后,則應該從新設置等待函數(shù)的參數(shù),對等待的句柄數(shù)組進行整理。

{

int nIndex=dRet-WAIT_OBJECT_0;

hArray[nIndex]=hArray[nWaitCount-1];

hArray[nWaitCount-1]=NULL;

nWaitCount--;

}

這組語句就是用來從新設置參數(shù)的,其過程就是將等待的總數(shù)目減一,并將剛退出的線程的句柄設置為NULL,移到數(shù)組的最末位置。

 

上面介紹了線程函數(shù)的設計以及在主線程中等待工作線程退出的方法,著重介紹了MsgWaitForMultipleObjects函數(shù)的使用要點,希望對大家有所幫助,也希望大家能提寶貴意見,補我之不足,愿與大家共同進步。

posted on 2009-04-01 01:04 大龍 閱讀(7853) 評論(6)  編輯 收藏 引用

評論

# re: 線程函數(shù)的設計以及MsgWaitForMultipleObjects函數(shù)的使用要點 ----- 轉[未登錄] 2009-08-05 14:05 kevin

好文章,解釋得很清楚  回復  更多評論   

# re: 線程函數(shù)的設計以及MsgWaitForMultipleObjects函數(shù)的使用要點 ----- 轉 2010-01-23 22:47 路過

::KillTimer(NULL,1);語句有問題,事實并沒有停止TImer。改為killTimer(NULL,nTimerID)  回復  更多評論   

# re: 線程函數(shù)的設計以及MsgWaitForMultipleObjects函數(shù)的使用要點 ----- 轉 2010-05-20 10:43 ztest8

GooD!   回復  更多評論   

# re: 線程函數(shù)的設計以及MsgWaitForMultipleObjects函數(shù)的使用要點 ----- 轉 2011-08-15 11:32 okay

int nIndex=dRet-WAIT_OBJECT_0;

hArray[nIndex]=hArray[nWaitCount-1];

hArray[nWaitCount-1]=NULL;

nWaitCount--;


此處有問題,對于多個線程,如果第一個到最后第二個可以這么寫,對于數(shù)組中的最后一個直接
hArray[nWaitCount-1]=NULL;

nWaitCount--;
就行了  回復  更多評論   

# re: 線程函數(shù)的設計以及MsgWaitForMultipleObjects函數(shù)的使用要點 ----- 轉 2012-12-19 10:15 12313

int nIndex=dRet-WAIT_OBJECT_0;

hArray[nIndex]=hArray[nWaitCount-1];

hArray[nWaitCount-1]=NULL;

nWaitCount--;

這個顯然有問題
你怎么知道 hArray[nWaitCount-1] 就不是hArray[nIndex]?如果nIndex 等于1的話,這幾句就錯了。  回復  更多評論   

# re: 線程函數(shù)的設計以及MsgWaitForMultipleObjects函數(shù)的使用要點 ----- 轉 2014-11-06 20:18 breeze

將hArray[nIndex]=hArray[nWaitCount-1];改為:
for (int i = 0; i < nWaitCount - 1; ++i) { //[更正]刪除數(shù)組中已退出的線程
if (i == nIndex) {
hArray[i]=hArray[nWaitCount-1];
break;
}
}
  回復  更多評論   


只有注冊用戶登錄后才能發(fā)表評論。
網(wǎng)站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            免费不卡在线观看av| 亚洲综合清纯丝袜自拍| 玖玖综合伊人| 欧美性猛交xxxx乱大交退制版| 香蕉av777xxx色综合一区| 欧美激情久久久久| 亚洲精品日产精品乱码不卡| 黄色精品一区二区| 午夜在线成人av| 国产一区二区三区在线观看免费视频 | av成人动漫| 亚洲乱码国产乱码精品精可以看 | 另类酷文…触手系列精品集v1小说| 香蕉成人啪国产精品视频综合网| 午夜在线视频一区二区区别| 香蕉国产精品偷在线观看不卡| 亚洲欧美不卡| 麻豆精品在线观看| 亚洲国产成人精品女人久久久| 欧美风情在线观看| 欧美本精品男人aⅴ天堂| 欧美二区不卡| 亚洲手机在线| 国产美女精品一区二区三区 | 中文日韩在线| 亚洲图中文字幕| 久久久久.com| 老司机午夜精品视频| 欧美日本精品在线| 国产午夜精品久久久久久免费视| 国产日韩精品一区二区三区在线| 国产日韩精品一区| 日韩午夜视频在线观看| 久久婷婷久久一区二区三区| 91久久中文字幕| 久久理论片午夜琪琪电影网| 国产精品成人在线观看| 亚洲国产日韩欧美在线动漫| 久久精品日产第一区二区| 欧美在线看片a免费观看| 国产一区成人| 亚洲国产精品久久| 欧美日韩专区| 久久精品国产久精国产思思| 久久精品91久久久久久再现| 国产亚洲午夜高清国产拍精品| 久久福利毛片| 欧美伦理影院| 欧美日韩亚洲在线| 欧美一区免费视频| 久久精品一区二区三区中文字幕| 亚洲老板91色精品久久| 午夜日韩视频| 亚洲一区国产精品| 久久久天天操| 久久久久成人精品| 国产伊人精品| 欧美激情aⅴ一区二区三区| 欧美精品不卡| 亚洲一区免费视频| 欧美刺激性大交免费视频| 一本色道久久99精品综合| 亚洲高清视频在线观看| 欧美日韩三级视频| 久久在线免费观看| 国产伦精品一区二区三区视频孕妇| 葵司免费一区二区三区四区五区| 欧美天堂亚洲电影院在线播放 | 美女亚洲精品| 欧美在线一二三四区| 国产精品v日韩精品| 亚洲国产福利在线| 伊人久久久大香线蕉综合直播| 欧美一区二区精品在线| 久久精品电影| 一区二区三区在线观看欧美| 午夜日韩在线观看| 久久精品国产久精国产一老狼| 国产精品综合av一区二区国产馆| 亚洲人成在线播放| 一本色道久久综合亚洲精品不卡| 欧美精品在线观看91| 亚洲欧洲一区二区在线观看| 一区二区欧美日韩视频| 国产精品久久久久久久免费软件 | 欧美专区18| 黄色综合网站| 欧美麻豆久久久久久中文| 一区二区日韩免费看| 国产精品女主播| 一区二区三区www| 久久国产精品第一页| 亚洲成人中文| 欧美色区777第一页| 性欧美8khd高清极品| 免费黄网站欧美| 亚洲你懂的在线视频| 亚洲国产成人久久综合一区| 欧美日韩妖精视频| 久久婷婷亚洲| 亚洲欧美日韩一区二区| 亚洲高清视频在线观看| 欧美亚洲专区| 亚洲香蕉网站| 亚洲精品在线观看免费| 国产欧美日韩不卡| 欧美日韩一区二区视频在线| 久久久亚洲午夜电影| 久久成人免费日本黄色| 亚洲欧美资源在线| 亚洲欧美精品suv| 日韩亚洲欧美在线观看| 亚洲高清不卡在线| 亚洲黄色免费网站| 欧美ab在线视频| 欧美成熟视频| 91久久久久久| 99re这里只有精品6| 亚洲美女中出| 亚洲一区二区成人在线观看| 99亚洲精品| 亚洲欧美综合国产精品一区| 亚洲自拍偷拍色片视频| 久久成人资源| 欧美激情第3页| 欧美四级在线| 国内外成人在线视频| 亚洲国产日韩欧美一区二区三区| a4yy欧美一区二区三区| 国产精品视频免费在线观看| 欧美日韩国产一区二区三区| 欧美综合77777色婷婷| 亚洲第一在线综合在线| 欧美手机在线| 日韩视频一区二区三区在线播放免费观看 | 亚洲在线第一页| 欧美91大片| 在线精品国产成人综合| 亚洲一区二区三区精品动漫| 欧美在线免费观看| 亚洲理伦在线| 久久免费99精品久久久久久| 欧美日韩中文字幕精品| 亚洲国产小视频| 亚洲欧美日本日韩| 亚洲人成7777| 久久久久久高潮国产精品视| 国产日韩欧美电影在线观看| 欧美在线观看你懂的| 亚洲欧美bt| 欧美午夜不卡影院在线观看完整版免费| 激情小说亚洲一区| 美女爽到呻吟久久久久| 久久午夜视频| 亚洲欧洲综合| 久久伊人精品天天| 欧美在线欧美在线| 狠狠久久婷婷| 欧美大片免费看| 欧美福利视频一区| 亚洲视频在线一区| 亚洲午夜精品久久| 狠狠久久亚洲欧美专区| 麻豆久久精品| 欧美日韩在线一区二区| 亚洲一线二线三线久久久| 亚洲一级一区| 亚洲激情图片小说视频| 亚洲女性裸体视频| 中日韩午夜理伦电影免费| 久久久久久综合| 欧美日韩精品中文字幕| 久久一区二区三区国产精品| 国产精品无人区| 亚洲欧美一区二区激情| 久久爱另类一区二区小说| 欧美日韩精品免费在线观看视频| 亚洲精品视频啊美女在线直播| 一区二区三区www| 欧美在线观看一二区| 亚洲美女色禁图| 欧美在线一二三四区| 99国产精品久久久久久久成人热| 亚洲综合精品| 99视频精品在线| 美日韩在线观看| 久久精品综合| 国产欧美在线观看一区| 日韩视频一区二区| 一区二区av在线| 久色婷婷小香蕉久久| 美女尤物久久精品| 狠狠色综合日日| 午夜视频一区二区| 久久精品午夜| 国产亚洲精品一区二区| 亚洲男人影院| 久久免费视频在线观看| 在线观看一区二区视频| 久久久久久久精|