• <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>

            大龍的博客

            常用鏈接

            統(tǒng)計

            最新評論

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

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

             

            創(chuàng)建線程

             

                   使用MFC提供的全局函數(shù)AfxBeginThread()即可創(chuàng)建一個工作線程。線程函數(shù)的標(biāo)準(zhǔn)形式為 UINT MyFunProc(LPVOID );此函數(shù)既可以是全局函數(shù),也可以是類的靜態(tài)成員函數(shù)。之所以必須是靜態(tài)成員函數(shù),是由于類的非靜態(tài)成員函數(shù),編譯器在編譯時會自動加上一個this指針參數(shù),如果將函數(shù)設(shè)置為靜態(tài)的成員函數(shù),則可以消除this指針參數(shù)。如果想在線程函數(shù)中任意調(diào)用類的成員變量(此處指的是數(shù)據(jù)成員,而不是控件關(guān)聯(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í)際的線程函數(shù)操作,卻可以自如的調(diào)用成員數(shù)據(jù)

            void CThreadTest::SetProgress()

            {

            int nCount=0;

                   while (1)

                   {

                          m_progress.SetPos(nCount); //設(shè)置進(jìn)度條進(jìn)度

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

                          nCount++;

                          if (g_exitThread)

                          {

                                 return;

                          }

                          Sleep(200);

                   }

            }

             

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

             

            有過多線程設(shè)計經(jīng)驗(yàn)的人都有體會,多線程設(shè)計最重要的就是要處理好線程間的同步和通訊問題。如解決不好這個問題,會給程序帶來潛藏的隱患。線程的同步可以利用臨界區(qū)、事件、互斥體和信號量來實(shí)現(xiàn),線程間的通訊可利用全局變量和發(fā)消息的形式實(shí)現(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)應(yīng)用事件變量的典型方式,也是推薦的方式。

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

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

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

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

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

            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

                          {

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

            //入空循環(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ā)消息,因?yàn)檫@個函數(shù)是同步操作,就是如果SendMessage函數(shù)不執(zhí)行完,是不會返回的,這樣線程函數(shù)就無法繼續(xù)執(zhí)行。有時這種操作容易導(dǎo)致工作線程和主線程死鎖,這個我們后面會談到,會介紹一種解決方法。

             

            線程的退出

             

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

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

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

             

            //退出線程

            void CThreadTest::OnButton2()

            {

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

             

            #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這組預(yù)編譯指令控制函數(shù)的執(zhí)行代碼,如果我令#if 1,則執(zhí)行WaitForSingleObject函數(shù),如果我令#if 0,則執(zhí)行DWORD dRet路徑。首先令#if  1,測試會發(fā)現(xiàn),程序死鎖了。原因是當(dāng)程序執(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)掛起,所以沒有機(jī)會去消息隊列中抓取消息并處理它,結(jié)果導(dǎo)致SetPos函數(shù)不會返回,工作線程也被掛起,典型的死鎖。如果不用m_progress.SetPos,而改用this->SendMessage(…),其結(jié)果是一樣的。此時如果用了PostMessage,則工作線程會順利退出,因?yàn)?/span>PostMessage是異步執(zhí)行的。由此可見,在主線程中用WaitForSingleObject等待工作線程退出是有很大隱患的。

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

            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ù)組指針。
             
            如果只要等待一個線程退出,則直接設(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-表示要等待的線程中任意一個退出了,或是有消息到達(dá)了,此函數(shù)均會返回。
            在上面的OnButton2()函數(shù)中,我要等待一個線程退出,將fWaitAll設(shè)置為
            FALSE,目的是無論是線程真的退出了,還是有消息到達(dá)了,該函數(shù)都能返回。
            如果將該fWaitAll設(shè)置為TRUE,那么函數(shù)返回的唯一條件是線程退出了,即便
            是有消息到來了,該函數(shù)也一樣不會返回。
             
            DWORD dwMilliseconds:等待的事件,單位是毫秒。可以設(shè)置為INFINITE,無
            窮等待
             
            DWORD dwWakeMask:等待的消息類型,通常可以設(shè)置為QS_ALLINPUT。此宏表示的是可以等待任意類型的消息。當(dāng)然,也可以指定等待的消息類型。
             
            #define QS_ALLINPUT        (QS_INPUT         | \
                                        QS_POSTMESSAGE   | \
                                        QS_TIMER         | \
                                        QS_PAINT         | \
                                        QS_HOTKEY        | \
                                        QS_SENDMESSAGE)
             

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

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

            dRet =WAIT_OBJECT_0+nCount:表示有消息到達(dá)了;

             

            如果fWaitAll設(shè)置為TRUE

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

            如果fWaitAll設(shè)置為FALSE

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

             

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

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

            {

                   TranslateMessage(&msg);

                   DispatchMessage(&msg);

            }

             

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

            //關(guān)閉線程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;       //標(biāo)記已經(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設(shè)置為FALSE,目的是當(dāng)我要等待的兩個線程中由一個退出了,或是有消息到來了,此函數(shù)都可以退出。如果我將此參數(shù)設(shè)置為TRUE,那么,當(dāng)且僅當(dāng)我要等待的兩個線程均退出了,這個函數(shù)才會返回,這種使用方法有是程序陷入死鎖的危險,故應(yīng)避免。無論是等待一個還是多個線程,只需將此參數(shù)設(shè)置為FALSE即可,然后通過函數(shù)返回值判斷究竟是那個返回了,還是消息到達(dá)了即可。這一要點(diǎn)前面已有陳述,此處再強(qiáng)調(diào)一遍。

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

            {

            int nIndex=dRet-WAIT_OBJECT_0;

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

            hArray[nWaitCount-1]=NULL;

            nWaitCount--;

            }

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

             

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

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

            評論

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

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

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

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

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

            GooD!   回復(fù)  更多評論   

            # re: 線程函數(shù)的設(shè)計以及MsgWaitForMultipleObjects函數(shù)的使用要點(diǎn) ----- 轉(zhuǎn) 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--;
            就行了  回復(fù)  更多評論   

            # re: 線程函數(shù)的設(shè)計以及MsgWaitForMultipleObjects函數(shù)的使用要點(diǎn) ----- 轉(zhuǎn) 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的話,這幾句就錯了。  回復(fù)  更多評論   

            # re: 線程函數(shù)的設(shè)計以及MsgWaitForMultipleObjects函數(shù)的使用要點(diǎn) ----- 轉(zhuǎn) 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ù)  更多評論   


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


            午夜精品久久久久久中宇| A级毛片无码久久精品免费| 一本色综合久久| 久久久无码精品亚洲日韩京东传媒 | 久久人人爽人人爽人人片AV麻豆| 亚洲欧美国产精品专区久久| 久久伊人中文无码| 18岁日韩内射颜射午夜久久成人| 日韩精品久久久肉伦网站 | 久久se精品一区精品二区国产| 久久久久亚洲?V成人无码| 国产精品成人久久久| 91久久九九无码成人网站| 久久无码国产专区精品| 精品综合久久久久久88小说| 久久妇女高潮几次MBA| 久久国产午夜精品一区二区三区| 亚洲精品美女久久久久99| 久久99精品国产麻豆婷婷| 狠狠色丁香久久婷婷综合| 国产激情久久久久影院小草| 久久国产精品成人影院| 国内精品伊人久久久久妇| 久久精品无码一区二区三区日韩| 狠狠色丁香婷综合久久| 7777精品久久久大香线蕉| 亚洲伊人久久大香线蕉苏妲己| 99久久精品国产麻豆| 97久久国产露脸精品国产| 精品国产婷婷久久久| 国产激情久久久久影院| 亚洲国产精品久久久久婷婷老年| 久久亚洲国产欧洲精品一| 99久久久国产精品免费无卡顿| 亚洲国产精品无码成人片久久| 久久亚洲精品国产精品婷婷| 久久天堂电影网| 人妻无码中文久久久久专区| 亚洲欧美日韩中文久久| 久久夜色精品国产噜噜噜亚洲AV| 国产精品欧美久久久天天影视|