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

            山寨:不是最好的,是最適合我們的!歡迎體驗(yàn)山寨 中文版MSDN

            Blog @ Blog

            當(dāng)華美的葉片落盡,生命的脈絡(luò)才歷歷可見(jiàn)。 -- 聶魯達(dá)

            常用鏈接

            統(tǒng)計(jì)

            積分與排名

            BBS

            Blog

            Web

            最新評(píng)論

            多線程與串行通信

            1 多任務(wù)、進(jìn)程和線程  
                Windows是一個(gè)多任務(wù)操作系統(tǒng)。傳統(tǒng)的Windows 3.x只能依靠應(yīng)用程序之間的協(xié)同來(lái)實(shí)現(xiàn)協(xié)同式多任務(wù),而Windows 95/NT實(shí)行的是搶先式多任務(wù)。

            在Win 32(95/NT)中,每一個(gè)進(jìn)程可以同時(shí)執(zhí)行多個(gè)線程,這意味著一個(gè)程序可以同時(shí)完成多個(gè)任務(wù)。對(duì)于象通信程序這樣既要進(jìn)行耗時(shí)的工作,又要保持對(duì)用戶 輸入響應(yīng)的應(yīng)用來(lái)說(shuō),使用多線程是最佳選擇。當(dāng)進(jìn)程使用多個(gè)線程時(shí),需要采取適當(dāng)?shù)拇胧﹣?lái)保持線程間的同步。

              利用Win 32的重疊I/O操作和多線程特性,程序員可以編寫(xiě)出高效的通信程序。在這一講的最后將通過(guò)一個(gè)簡(jiǎn)單的串行通信程序,向讀者演示多線程和重疊I/O的編程技術(shù)。

            1.1 Windows 3.x的協(xié)同多任務(wù)

            在16位的Windows 3.x中,應(yīng)用程序具有對(duì)CPU的控制權(quán)。只有在調(diào)用了GetMessage、PeekMessage、WaitMessage或Yield后,程序才有 可能把CPU控制權(quán)交給系統(tǒng),系統(tǒng)再把控制權(quán)轉(zhuǎn)交給別的應(yīng)用程序。如果應(yīng)用程序在長(zhǎng)時(shí)間內(nèi)無(wú)法調(diào)用上述四個(gè)函數(shù)之一,那么程序就一直獨(dú)占CPU,系統(tǒng)會(huì)被 掛起而無(wú)法接受用戶的輸入。

              因此,在設(shè)計(jì)16位的應(yīng)用程序時(shí),程序員必須合理地設(shè)計(jì)消息處理函數(shù),以使程序能夠盡快返回到消息循環(huán)中。如果程序需要進(jìn)行費(fèi)時(shí)的操作,那么必須保證程序在進(jìn)行操作時(shí)能周期性的調(diào)用上述四個(gè)函數(shù)中的一個(gè)。

              在Windows 3.x環(huán)境下,要想設(shè)計(jì)一個(gè)既能執(zhí)行實(shí)時(shí)的后臺(tái)工作(如對(duì)通信端口的實(shí)時(shí)監(jiān)測(cè)和讀寫(xiě)),又能保證所有界面響應(yīng)用戶輸入的單獨(dú)的應(yīng)用程序幾乎是不可能的。

            有人可能會(huì)想到用CWinApp::OnIdle函數(shù)來(lái)執(zhí)行后臺(tái)工作,因?yàn)樵摵瘮?shù)是程序主消息循環(huán)在空閑時(shí)調(diào)用的。但OnIdle的執(zhí)行并不可靠,例 如,如果用戶在程序中打開(kāi)了一個(gè)菜單或模態(tài)對(duì)話框,那么OnIdle將停止調(diào)用,因?yàn)榇藭r(shí)程序不能返回到主消息循環(huán)中!在實(shí)時(shí)任務(wù)代碼中調(diào)用 PeekMessage也會(huì)遇到同樣的問(wèn)題,除非程序能保證用戶不會(huì)選擇菜單或彈出模態(tài)對(duì)話框,否則程序?qū)⒉荒芊祷氐絇eekMessage的調(diào)用處,這 將導(dǎo)致后臺(tái)實(shí)時(shí)處理的中斷。

              折衷的辦法是在執(zhí)行長(zhǎng)期工作時(shí)彈出一個(gè)非模態(tài)對(duì) 話框并禁止主窗口,在消息循環(huán)內(nèi)分批執(zhí)行后臺(tái)操作。對(duì)話框中可以顯示工作的進(jìn)度,也可以包含一個(gè)取消按鈕以讓用戶有機(jī)會(huì)中斷一個(gè)長(zhǎng)期的工作。典型的代碼如 清單12.1所示。這樣做既可以保證工作實(shí)時(shí)進(jìn)行,又可以使程序能有限地響應(yīng)用戶輸入,但此時(shí)程序?qū)嶋H上已不能再為用戶干別的事情了。


            //清單12.1 在協(xié)同多任務(wù)環(huán)境下防止程序被掛起的一種方法

            bAbort
            =FALSE;
            lpMyDlgProc
            =MakeProcInstance(MyDlgProc, hInst);
            hMyDlg
            =CreateDialog(hInst, “Abort”, hwnd, lpMyDlgProc); //創(chuàng)建一個(gè)非模態(tài)對(duì)話框
            ShowWindow(hMyDlg, SW_NORMAL);
            UpdateWindow(hMyDlg);
            EnableWindow(hwnd, FALSE); 
            //禁止主窗口

            while(!bAbort)
            {
                
            //執(zhí)行一次后臺(tái)操作        
                while(PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))        
                {        
                    
            if(!IsDialogMessage(hMyDlg, &msg))            
                    {        
                        TranslateMessage(
            &msg);        
                        DispatchMessage(
            &msg);
                    }
                }    
            }

            EnableWindow(hwnd, TRUE); 
            //允許主窗口
            DestroyWindow(hMyDlg);
            FreeProcInstance(lpMyDlgProc);

             

            1.2 Windows 95/NT的搶先式多任務(wù)

              在32位的Windows系統(tǒng)中,采用的是搶先式多任務(wù),這意味著程序?qū)PU的占用時(shí)間是由系統(tǒng)決定的。系統(tǒng)為每個(gè)程序分配一定的CPU時(shí)間,當(dāng)程序的運(yùn)行超過(guò)規(guī)定時(shí)間后,系統(tǒng)就會(huì)中斷該程序并把CPU控制權(quán)轉(zhuǎn)交給別的程序。與協(xié)同式多任務(wù)不同,這種中斷是匯編語(yǔ)言級(jí)的。程序不必調(diào)用象 PeekMessage這樣的函數(shù)來(lái)放棄對(duì)CPU的控制權(quán),就可以進(jìn)行費(fèi)時(shí)的工作,而且不會(huì)導(dǎo)致系統(tǒng)的掛起。
              例如,在Windows3.x 中,如果某一個(gè)應(yīng)用程序陷入了死循環(huán),那么整個(gè)系統(tǒng)都會(huì)癱瘓,這時(shí)唯一的解決辦法就是重新啟動(dòng)機(jī)器。而在Windows 95/NT中,一個(gè)程序的崩潰一般不會(huì)造成死機(jī),其它程序仍然可以運(yùn)行,用戶可以按Ctrl+Alt+Del鍵來(lái)打開(kāi)任務(wù)列表并關(guān)閉沒(méi)有響應(yīng)的程序。
            1.3 進(jìn)程與線程

              在32位的Windows系統(tǒng)中,術(shù)語(yǔ)多任務(wù)是指系統(tǒng)可以同時(shí)運(yùn)行多個(gè)進(jìn)程,而每個(gè)進(jìn)程也可以同時(shí)執(zhí)行多個(gè)線程。
              進(jìn)程就是應(yīng)用程序的運(yùn)行實(shí)例。每個(gè)進(jìn)程都有自己私有的虛擬地址空間。每個(gè)進(jìn)程都有一個(gè)主線程,但可以建立另外的線程。進(jìn)程中的線程是并行執(zhí)行的,每個(gè)線程占用CPU的時(shí)間由系統(tǒng)來(lái)劃分。
              可以把線程看成是操作系統(tǒng)分配CPU時(shí)間的基本實(shí)體。系統(tǒng)不停地在各個(gè)線程之間切換,它對(duì)線程的中斷是匯編語(yǔ)言級(jí)的。系統(tǒng)為每一個(gè)線程分配一個(gè)CPU時(shí)間片,某個(gè)線程只有在分配的時(shí)間片內(nèi)才有對(duì)CPU的控制權(quán)。實(shí)際上,在PC機(jī)中,同一時(shí)間只有一個(gè)線程在運(yùn)行。由于系統(tǒng)為每個(gè)線程劃分的時(shí)間片很?。?0 毫秒左右),所以看上去好象是多個(gè)線程在同時(shí)運(yùn)行。
              進(jìn)程中的所有線程共享進(jìn)程的虛擬地址空間,這意味著所有線程都可以訪問(wèn)進(jìn)程的全局變量和資源。這一方面為編程帶來(lái)了方便,但另一方面也容易造成沖突。
              雖然在進(jìn)程中進(jìn)行費(fèi)時(shí)的工作不會(huì)導(dǎo)致系統(tǒng)的掛起,但這會(huì)導(dǎo)致進(jìn)程本身的掛起。所以,如果進(jìn)程既要進(jìn)行長(zhǎng)期的工作,又要響應(yīng)用戶的輸入,那么它可以啟動(dòng)一個(gè)線程來(lái)專門(mén)負(fù)責(zé)費(fèi)時(shí)的工作,而主線程仍然可以與用戶進(jìn)行交互。
            1.4 線程的創(chuàng)建和終止

              線程分用戶界面線程和工作者線程兩種。用戶界面線程擁有自己的消息泵來(lái)處理界面消息,可以與用戶進(jìn)行交互。工作者線程沒(méi)有消息泵,一般用來(lái)完成后臺(tái)工作。

              MFC應(yīng)用程序的線程由對(duì)象CWinThread表示。在多數(shù)情況下,程序不需要自己創(chuàng)建CWinThread對(duì)象。調(diào)用AfxBeginThread函數(shù)時(shí)會(huì)自動(dòng)創(chuàng)建一個(gè)CWinThread對(duì)象。

            例如,清單12.2中的代碼演示了工作者線程的創(chuàng)建。AfxBeginThread函數(shù)負(fù)責(zé)創(chuàng)建新線程,它的第一個(gè)參數(shù)是代表線程的函數(shù)的地址,在本例 中是MyThreadProc。第二個(gè)參數(shù)是傳遞給線程函數(shù)的參數(shù),這里假定線程要用到CMyObject對(duì)象,所以把pNewObject指針傳給了新 線程。線程函數(shù)MyThreadProc用來(lái)執(zhí)行線程,請(qǐng)注意該函數(shù)的聲明。線程函數(shù)有一個(gè)32位的pParam參數(shù)可用來(lái)接收必要的參數(shù)。

            清單12.2 創(chuàng)建一個(gè)工作者線程

            //主線程

            pNewObject 
            = new CMyObject;
            AfxBeginThread(MyThreadProc, pNewObject);
            //新線程
            UINT MyThreadProc( LPVOID pParam )
            {
                CMyObject
            * pObject = (CMyObject*)pParam;
                    
            if (pObject == NULL || !pObject->IsKindOf(RUNTIME_CLASS(CMyObject)))
                        
            return -1// 非法參數(shù)
                    
            // 用pObject對(duì)象來(lái)完成某項(xiàng)工作
                    return 0// 線程正常結(jié)束
            }

             

            AfxBeginThread的聲明為:
            CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

            參數(shù)pfnThreadProc是工作線程函數(shù)的地址。pParam是傳遞給線程函數(shù)的參數(shù)。nPriority 是線程的優(yōu)先級(jí),一般是THREAD_PRIORITY_NORMAL,若為0,則使用創(chuàng)建線程的優(yōu)先級(jí)。nStackSize說(shuō)明了線程的堆棧尺寸,若 為0則堆棧尺寸與創(chuàng)建線程相同。dwCreateFlags指定了線程的初始狀態(tài),如果為0,那么線程在創(chuàng)建后立即執(zhí)行,如果為 CREATE_SUSPENDED,則線程在創(chuàng)建后就被掛起。參數(shù)lpSecurityAttrs用來(lái)說(shuō)明保密屬性,一般為0。函數(shù)返回新建的 CWinThread對(duì)象的指針。

              程序應(yīng)該把AfxBeginThread 返回的CWinThread指針保存起來(lái),以便對(duì)創(chuàng)建的線程進(jìn)行控制。例如,可以調(diào)用CWinThread::SetThreadPriority來(lái)設(shè)置 線程的優(yōu)先級(jí),用CWinThread::SuspendThread來(lái)掛起線程。如果線程被掛起,那么直到調(diào)用CWinThread:: ResumeThread后線程才開(kāi)始運(yùn)行。

              如果要?jiǎng)?chuàng)建用戶界面線程,那么 必須從CWinThread派生一個(gè)新類。事實(shí)上,代表進(jìn)程主線程的CWinApp類就是CWinThread的派生類。派生類必須用 DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏來(lái)聲明和實(shí)現(xiàn)。需要重寫(xiě)派生類的InitInstance、 ExitInstance、Run等函數(shù)。

              可以使用AfxBeginThread函數(shù)的另一個(gè)版本來(lái)創(chuàng)建用戶界面線程。函數(shù)的聲明為:

            CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );

              參數(shù)pThreadClass指向一個(gè)CRuntimeClass對(duì)象,該對(duì)象是用RUNTIME_CLASS宏從CWinThread的派生類創(chuàng)建的。其它參數(shù)以及函數(shù)的返回值與第一個(gè)版本的AfxBeginThread是一樣的。
               當(dāng)發(fā)生下列事件之一時(shí),線程被終止:
            線程調(diào)用ExitThread。
            線程函數(shù)返回,即線程隱含調(diào)用了ExitThread。
            ExitProcess被進(jìn)程的任一線程顯示或隱含調(diào)用。
            用線程的句柄調(diào)用TerminateThread。
            用進(jìn)程句柄調(diào)用TerminateProcess。

            2 線程的同步

            多線程的使用會(huì)產(chǎn)生一些新的問(wèn)題,主要是如何保證線程的同步執(zhí)行。多線程應(yīng)用程序需要使用同步對(duì)象和等待函數(shù)來(lái)實(shí)現(xiàn)同步。

            12.2.1 為什么需要同步

            由于同一進(jìn)程的所有線程共享進(jìn)程的虛擬地址空間,并且線程的中斷是匯編語(yǔ)言級(jí)的,所以可能會(huì)發(fā)生兩個(gè)線程同時(shí)訪問(wèn)同一個(gè)對(duì)象(包括全局變量、共享資源、 API函數(shù)和MFC對(duì)象等)的情況,這有可能導(dǎo)致程序錯(cuò)誤。例如,如果一個(gè)線程在未完成對(duì)某一大尺寸全局變量的讀操作時(shí),另一個(gè)線程又對(duì)該變量進(jìn)行了寫(xiě)操 作,那么第一個(gè)線程讀入的變量值可能是一種修改過(guò)程中的不穩(wěn)定值。

              屬于不同進(jìn)程的線程在同時(shí)訪問(wèn)同一內(nèi)存區(qū)域或共享資源時(shí),也會(huì)存在同樣的問(wèn)題。

              因此,在多線程應(yīng)用程序中,常常需要采取一些措施來(lái)同步線程的執(zhí)行。需要同步的情況包括以下幾種:

            在多個(gè)線程同時(shí)訪問(wèn)同一對(duì)象時(shí),可能產(chǎn)生錯(cuò)誤。例如,如果當(dāng)一個(gè)線程正在讀取一個(gè)至關(guān)重要的共享緩沖區(qū)時(shí),另一個(gè)線程向該緩沖區(qū)寫(xiě)入數(shù)據(jù),那么程序的運(yùn)行結(jié)果就可能出錯(cuò)。程序應(yīng)該盡量避免多個(gè)線程同時(shí)訪問(wèn)同一個(gè)緩沖區(qū)或系統(tǒng)資源。

            在Windows 95環(huán)境下編寫(xiě)多線程應(yīng)用程序還需要考慮重入問(wèn)題。Windows NT是真正的32位操作系統(tǒng),它解決了系統(tǒng)重入問(wèn)題。而Windows 95由于繼承了Windows 3.x的部分16位代碼,沒(méi)能夠解決重入問(wèn)題。這意味著在Windows 95中兩個(gè)線程不能同時(shí)執(zhí)行某個(gè)系統(tǒng)功能,否則有可能造成程序錯(cuò)誤,甚至?xí)斐上到y(tǒng)崩潰。應(yīng)用程序應(yīng)該盡量避免發(fā)生兩個(gè)以上的線程同時(shí)調(diào)用同一個(gè) Windows API函數(shù)的情況。

            由于大小和性能方面的原因,MFC對(duì)象在對(duì)象級(jí)不是線程安全的,只有在類級(jí)才是。也就是說(shuō),兩個(gè)線程可以安全地使用兩個(gè)不同的CString對(duì)象,但同時(shí)使用同一個(gè)CString對(duì)象就可能產(chǎn)生問(wèn)題。如果必須使用同一個(gè)對(duì)象,那么應(yīng)該采取適當(dāng)?shù)耐酱胧?/p>

            多個(gè)線程之間需要協(xié)調(diào)運(yùn)行。例如,如果第二個(gè)線程需要等待第一個(gè)線程完成到某一步時(shí)才能運(yùn)行,那么該線程應(yīng)該暫時(shí)掛起以減少對(duì)CPU的占用時(shí)間,提高程序的執(zhí)行效率。當(dāng)?shù)谝粋€(gè)線程完成了相應(yīng)的步驟后,應(yīng)該發(fā)出某種信號(hào)來(lái)激活第二個(gè)線程。

             

            12.2.2 等待函數(shù)

            Win32 API提供了一組能使線程阻塞其自身執(zhí)行的等待函數(shù)。這些函數(shù)只有在作為其參數(shù)的一個(gè)或多個(gè)同步對(duì)象(見(jiàn)下小節(jié))產(chǎn)生信號(hào)時(shí)才會(huì)返回。在超過(guò)規(guī)定的等待時(shí) 間后,不管有無(wú)信號(hào),函數(shù)也都會(huì)返回。在等待函數(shù)未返回時(shí),線程處于等待狀態(tài),此時(shí)線程只消耗很少的CPU時(shí)間。

              使用等待函數(shù)即可以保證線程的同步,又可以提高程序的運(yùn)行效率。最常用的等待函數(shù)是WaitForSingleObject,該函數(shù)的聲明為:

            DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);

              參數(shù)hHandle是同步對(duì)象的句柄。參數(shù)dwMilliseconds是以毫秒為單位的超時(shí)間隔,如果該參數(shù)為0,那么函數(shù)就測(cè)試同步對(duì)象的狀態(tài)并立即返回,如果該參數(shù)為INFINITE,則超時(shí)間隔是無(wú)限的。函數(shù)的返回值在表12.1中列出。

             

            表12.1 WaitForSingleObject的返回值

            返回值

            含義

            WAIT_FAILED

            函數(shù)失敗

            WAIT_OBJECT_0

            指定的同步對(duì)象處于有信號(hào)的狀態(tài)

            WAIT_ABANDONED

            擁有一個(gè)mutex的線程已經(jīng)中斷了,但未釋放該MUTEX

            WAIT_TIMEOUT

            超時(shí)返回,并且同步對(duì)象無(wú)信號(hào)

             

             

            函數(shù)WaitForMultipleObjects可以同時(shí)監(jiān)測(cè)多個(gè)同步對(duì)象,該函數(shù)的聲明為:

            DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds );

             

            參數(shù)nCount是句柄數(shù)組中句柄的數(shù)目。lpHandles代表一個(gè)句柄數(shù)組。bWaitAll說(shuō)明了等待類型,如果為T(mén)RUE,那么函數(shù)在所有對(duì)象 都有信號(hào)后才返回,如果為FALSE,則只要有一個(gè)對(duì)象變成有信號(hào)的,函數(shù)就返回。函數(shù)的返回值在表12.2中列出。參數(shù)dwMilliseconds是 以毫秒為單位的超時(shí)間隔,如果該參數(shù)為0,那么函數(shù)就測(cè)試同步對(duì)象的狀態(tài)并立即返回,如果該參數(shù)為INFINITE,則超時(shí)間隔是無(wú)限的。

             

            表12.2 WaitForMultipleObjects的返回值

            返回值

            說(shuō)明

            WAIT_OBJECT_0到WAIT_ OBJECT_0+nCount-1

            若bWaitAll為T(mén)RUE,則返回值表明所有對(duì)象都是有信號(hào)的。如果bWaitAll為FALSE,則返回值減去WAIT_OBJECT_0就是數(shù)組中有信號(hào)對(duì)象的最小索引。

            WAIT_ABANDONED_0到WAIT_ ABANDONED_ 0+nCount-1

            若bWaitAll為T(mén)RUE,則返回值表明所有對(duì)象都有信號(hào),但有一個(gè)mutex被放棄了。若bWaitAll為FALSE,則返回值減去WAIT_ABANDONED_0就是被放棄mutex在對(duì)象數(shù)組中的索引。

            WAIT_TIMEOUT

            超時(shí)返回。

             

            12.2.3 同步對(duì)象

              同步對(duì)象用來(lái)協(xié)調(diào)多線程的執(zhí)行,它可以被多個(gè)線程共享。線程的等待函數(shù)用同步對(duì)象的句柄作為參數(shù),同步對(duì)象應(yīng)該是所有要使用的線程都能訪問(wèn)到的。同步對(duì)象的狀態(tài)要么是有信號(hào)的,要么是無(wú)信號(hào)的。同步對(duì)象主要有三種:事件、mutex和信號(hào)燈。

              事件對(duì)象(Event)是最簡(jiǎn)單的同步對(duì)象,它包括有信號(hào)和無(wú)信號(hào)兩種狀態(tài)。在線程訪問(wèn)某一資源之前,也許需要等待某一事件的發(fā)生,這時(shí)用事件對(duì)象最合適。例如,只有在通信端口緩沖區(qū)收到數(shù)據(jù)后,監(jiān)視線程才被激活。

            事件對(duì)象是用CreateEvent函數(shù)建立的。該函數(shù)可以指定事件對(duì)象的種類和事件的初始狀態(tài)。如果是手工重置事件,那么它總是保持有信號(hào)狀態(tài),直到 用ResetEvent函數(shù)重置成無(wú)信號(hào)的事件。如果是自動(dòng)重置事件,那么它的狀態(tài)在單個(gè)等待線程釋放后會(huì)自動(dòng)變?yōu)闊o(wú)信號(hào)的。用SetEvent可以把事 件對(duì)象設(shè)置成有信號(hào)狀態(tài)。在建立事件時(shí),可以為對(duì)象起個(gè)名字,這樣其它進(jìn)程中的線程可以用OpenEvent函數(shù)打開(kāi)指定名字的事件對(duì)象句柄。

              mutex對(duì)象的狀態(tài)在它不被任何線程擁有時(shí)是有信號(hào)的,而當(dāng)它被擁有時(shí)則是無(wú)信號(hào)的。mutex對(duì)象很適合用來(lái)協(xié)調(diào)多個(gè)線程對(duì)共享資源的互斥訪問(wèn)(mutually exclusive)。

            線程用CreateMutex函數(shù)來(lái)建立mutex對(duì)象,在建立mutex時(shí),可以為對(duì)象起個(gè)名字,這樣其它進(jìn)程中的線程可以用OpenMutex函數(shù) 打開(kāi)指定名字的mutex對(duì)象句柄。在完成對(duì)共享資源的訪問(wèn)后,線程可以調(diào)用ReleaseMutex來(lái)釋放mutex,以便讓別的線程能訪問(wèn)共享資源。 如果線程終止而不釋放mutex,則認(rèn)為該mutex被廢棄。

              信號(hào)燈對(duì)象維 護(hù)一個(gè)從0開(kāi)始的計(jì)數(shù),在計(jì)數(shù)值大于0時(shí)對(duì)象是有信號(hào)的,而在計(jì)數(shù)值為0時(shí)則是無(wú)信號(hào)的。信號(hào)燈對(duì)象可用來(lái)限制對(duì)共享資源進(jìn)行訪問(wèn)的線程數(shù)量。線程用 CreateSemaphore函數(shù)來(lái)建立信號(hào)燈對(duì)象,在調(diào)用該函數(shù)時(shí),可以指定對(duì)象的初始計(jì)數(shù)和最大計(jì)數(shù)。在建立信號(hào)燈時(shí)也可以為對(duì)象起個(gè)名字,別的進(jìn) 程中的線程可以用OpenSemaphore函數(shù)打開(kāi)指定名字的信號(hào)燈句柄。

              一般把信號(hào)燈的初始計(jì)數(shù)設(shè)置成最大值。每次當(dāng)信號(hào)燈有信號(hào)使等待函數(shù)返回時(shí),信號(hào)燈計(jì)數(shù)就會(huì)減1,而調(diào)用ReleaseSemaphore可以增加信號(hào)燈的計(jì)數(shù)。計(jì)數(shù)值越小就表明訪問(wèn)共享資源的程序越多。

              除了上述三種同步對(duì)象外,表12.3中的對(duì)象也可用于同步。另外,有時(shí)可以用文件或通信設(shè)備作為同步對(duì)象使用。

             

            表12.3 可用于同步的對(duì)象

            對(duì)象

            描述

            變化通知

            由FindFirstChangeNotification函數(shù)建立,當(dāng)在指定目錄中發(fā)生指定類型的變化時(shí)對(duì)象變成有信號(hào)的。

            控制臺(tái)輸入

            在控制臺(tái)建立是被創(chuàng)建。它是用CONIN$調(diào)用CreateFile函數(shù)返回的句柄,或是GetStdHandle函數(shù)的返回句柄。如果控制臺(tái)輸入緩沖區(qū)中有數(shù)據(jù),那么對(duì)象是有信號(hào)的,如果緩沖區(qū)為空,則對(duì)象是無(wú)信號(hào)的。

            進(jìn)程

            當(dāng)調(diào)用CreateProcess建立進(jìn)程時(shí)被創(chuàng)建。進(jìn)程在運(yùn)行時(shí)對(duì)象是無(wú)信號(hào)的,當(dāng)進(jìn)程終止時(shí)對(duì)象是有信號(hào)的。

            線程

            當(dāng)調(diào)用Createprocess、CreateThread或CreateRemoteThread函數(shù)創(chuàng)建新線程時(shí)被創(chuàng)建。在線程運(yùn)行是對(duì)象是無(wú)信號(hào)的,在線程終止時(shí)則是有信號(hào)的。

             

             

              當(dāng)對(duì)象不再使用時(shí),應(yīng)該用CloseHandle函數(shù)關(guān)閉對(duì)象句柄。

            清單12.3是一個(gè)使用事件對(duì)象的簡(jiǎn)單例子,在該例中,假設(shè)主線程要讀取共享緩沖區(qū)中的內(nèi)容,而輔助線程負(fù)責(zé)向緩沖區(qū)中寫(xiě)入數(shù)據(jù)。兩個(gè)線程使用了一個(gè) hEvent事件對(duì)象來(lái)同步。在用CreateEvent函數(shù)創(chuàng)建事件對(duì)象句柄時(shí),指定該對(duì)象是一個(gè)自動(dòng)重置事件,其初始狀態(tài)為有信號(hào)的。當(dāng)線程要讀寫(xiě)緩 沖區(qū)時(shí),調(diào)用WaitForSingleObject函數(shù)無(wú)限等待hEvent信號(hào)。如果hEvent無(wú)信號(hào),則說(shuō)明另一線程正在訪問(wèn)緩沖區(qū);如果有信 號(hào),則本線程可以訪問(wèn)緩沖區(qū),WaitForSingleObject函數(shù)在返回后會(huì)自動(dòng)把hEvent置成無(wú)信號(hào)的,這樣在本線程讀寫(xiě)緩沖區(qū)時(shí)別的線程 不會(huì)同時(shí)訪問(wèn)。在完成讀寫(xiě)操作后,調(diào)用SetEvent函數(shù)把hEvent置成有信號(hào)的,以使別的線程有機(jī)會(huì)訪問(wèn)共享緩沖區(qū)。

             

            清單12.3 使用事件對(duì)象的簡(jiǎn)單例子

            HANDLE hEvent; 
            //全局變量
            //主線程
            hEvent=CreateEvent(NULL, FALSE, TRUE, NULL);
            if(hEvent= =NULL) return;
            WaitForSingleObject(hEvent, INFINITE);
            ReadFromBuf( );
            SetEvent( hEvent );
            CloseHandle( hEvent );

            //輔助線程
            UINT MyThreadProc( LPVOID pParam )

            {
                . . .
                WaitForSingleObject(hEvent, INFINITE);
                WriteToBuf( );
                SetEvent( hEvent );
                . . .
                    
                
            return 0// 線程正常結(jié)束
            }

            12.2.4 關(guān)鍵節(jié)和互鎖變量訪問(wèn)

              關(guān)鍵節(jié)(Critical Seciton)與mutex的功能類似,但它只能由同一進(jìn)程中的線程使用。關(guān)鍵節(jié)可以防止共享資源被同時(shí)訪問(wèn)。

            進(jìn)程負(fù)責(zé)為關(guān)鍵節(jié)分配內(nèi)存空間,關(guān)鍵節(jié)實(shí)際上是一個(gè)CRITICAL_SECTION型的變量,它一次只能被一個(gè)線程擁有。在線程使用關(guān)鍵節(jié)之前,必須 調(diào)用InitializeCriticalSection函數(shù)將其初始化。如果線程中有一段關(guān)鍵的代碼不希望被別的線程中斷,那么可以調(diào)用 EnterCriticalSection函數(shù)來(lái)申請(qǐng)關(guān)鍵節(jié)的所有權(quán),在運(yùn)行完關(guān)鍵代碼后再用LeaveCriticalSection函數(shù)來(lái)釋放所有 權(quán)。如果在調(diào)用EnterCriticalSection時(shí)關(guān)鍵節(jié)對(duì)象已被另一個(gè)線程擁有,那么該函數(shù)將無(wú)限期等待所有權(quán)。

            利用互鎖變量可以建立簡(jiǎn)單有效的同步機(jī)制。使用函數(shù)InterlockedIncrement和InterlockedDecrement可以增加或減 少多個(gè)線程共享的一個(gè)32位變量的值,并且可以檢查結(jié)果是否為0。線程不必?fù)?dān)心會(huì)被其它線程中斷而導(dǎo)致錯(cuò)誤。如果變量位于共享內(nèi)存中,那么不同進(jìn)程中的線 程也可以使用這種機(jī)制。

            3 串行通信與重疊I/O

            Win 32系統(tǒng)為串行通信提供了全新的服務(wù)。傳統(tǒng)的OpenComm、ReadComm、WriteComm、CloseComm等函數(shù)已經(jīng)過(guò)時(shí),WM_COMMNOTIFY消息也消失了。取而代之的是文件I/O函數(shù)提供的打開(kāi)和關(guān)閉通信資源句柄及讀寫(xiě)操作的基本接口。

              新的文件I/O函數(shù)(CreateFile、ReadFile、WriteFile等)支持重疊式輸入輸出,這使得線程可以從費(fèi)時(shí)的I/O操作中解放出來(lái),從而極大地提高了程序的運(yùn)行效率。

            12.3.1 串行口的打開(kāi)和關(guān)閉

              Win 32系統(tǒng)把文件的概念進(jìn)行了擴(kuò)展。無(wú)論是文件、通信設(shè)備、命名管道、郵件槽、磁盤(pán)、還是控制臺(tái),都是用API函數(shù)CreateFile來(lái)打開(kāi)或創(chuàng)建的。該函數(shù)的聲明為:

            HANDLE CreateFile(

            LPCTSTR lpFileName, // 文件名

            DWORD dwDesiredAccess, // 訪問(wèn)模式

            DWORD dwShareMode, // 共享模式

            LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 通常為NULL

            DWORD dwCreationDistribution, // 創(chuàng)建方式

            DWORD dwFlagsAndAttributes, // 文件屬性和標(biāo)志

            HANDLE hTemplateFile // 臨時(shí)文件的句柄,通常為NULL

            );

              如果調(diào)用成功,那么該函數(shù)返回文件的句柄,如果調(diào)用失敗,則函數(shù)返回INVALID_HANDLE_VALUE。

            如果想要用重疊I/O方式(參見(jiàn)12.3.3)打開(kāi)COM2口,則一般應(yīng)象清單12.4那樣調(diào)用CreateFile函數(shù)。注意在打開(kāi)一個(gè)通信端口時(shí), 應(yīng)該以獨(dú)占方式打開(kāi),另外要指定GENERIC_READ、GENERIC_WRITE、OPEN_EXISTING和 FILE_ATTRIBUTE_NORMAL等屬性。如果要打開(kāi)重疊I/O,則應(yīng)該指定 FILE_FLAG_OVERLAPPED屬性。

             

            清單12.4

            HANDLE hCom;
            DWORD dwError;
            hCom
            =CreateFile(“COM2”,  //文件名
                            GENERIC_READ | GENERIC_WRITE, // 允許讀和寫(xiě)    
                            0// 獨(dú)占方式
                            NULL,
                            OPEN_EXISTING, 
            //打開(kāi)而不是創(chuàng)建
                            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // 重疊方式
                            NULL
                            );

            if(hCom = = INVALID_HANDLE_VALUE)
            {
                dwError
            =GetLastError( );    
                . . . 
            // 處理錯(cuò)誤
            }

            當(dāng)不再使用文件句柄時(shí),應(yīng)該調(diào)用CloseHandle函數(shù)關(guān)閉之。

            12.3.2 串行口的初始化

              在打開(kāi)通信設(shè)備句柄后,常常需要對(duì)串行口進(jìn)行一些初始化工作。這需要通過(guò)一個(gè)DCB結(jié)構(gòu)來(lái)進(jìn)行。DCB結(jié)構(gòu)包含了諸如波特率、每個(gè)字符的數(shù)據(jù)位數(shù)、奇偶校驗(yàn)和停止位數(shù)等信息。在查詢或配置置串行口的屬性時(shí),都要用DCB結(jié)構(gòu)來(lái)作為緩沖區(qū)。

            調(diào)用GetCommState函數(shù)可以獲得串口的配置,該函數(shù)把當(dāng)前配置填充到一個(gè)DCB結(jié)構(gòu)中。一般在用CreateFile打開(kāi)串行口后,可以調(diào)用 GetCommState函數(shù)來(lái)獲取串行口的初始配置。要修改串行口的配置,應(yīng)該先修改DCB結(jié)構(gòu),然后再調(diào)用SetCommState函數(shù)用指定的 DCB結(jié)構(gòu)來(lái)設(shè)置串行口。

              除了在DCB中的設(shè)置外,程序一般還需要設(shè)置I/O緩沖區(qū)的大小和超時(shí)。Windows用I/O緩沖區(qū)來(lái)暫存串行口輸入和輸出的數(shù)據(jù),如果通信的速率較高,則應(yīng)該設(shè)置較大的緩沖區(qū)。調(diào)用SetupComm函數(shù)可以設(shè)置串行口的輸入和輸出緩沖區(qū)的大小。

            在用ReadFile和WriteFile讀寫(xiě)串行口時(shí),需要考慮超時(shí)問(wèn)題。如果在指定的時(shí)間內(nèi)沒(méi)有讀出或?qū)懭胫付〝?shù)量的字符,那么ReadFile或 WriteFile的操作就會(huì)結(jié)束。要查詢當(dāng)前的超時(shí)設(shè)置應(yīng)調(diào)用GetCommTimeouts函數(shù),該函數(shù)會(huì)填充一個(gè)COMMTIMEOUTS結(jié)構(gòu)。調(diào) 用SetCommTimeouts可以用某一個(gè)COMMTIMEOUTS結(jié)構(gòu)的內(nèi)容來(lái)設(shè)置超時(shí)。

              有兩種超時(shí):間隔超時(shí)和總超時(shí)。間隔超時(shí)是指在接收時(shí)兩個(gè)字符之間的最大時(shí)延,總超時(shí)是指讀寫(xiě)操作總共花費(fèi)的最大時(shí)間。寫(xiě)操作只支持總超時(shí),而讀操作兩種超時(shí)均支持。用COMMTIMEOUTS結(jié)構(gòu)可以規(guī)定讀/寫(xiě)操作的超時(shí),該結(jié)構(gòu)的定義為:

            typedef struct _COMMTIMEOUTS {

            DWORD ReadIntervalTimeout; // 讀間隔超時(shí)

            DWORD ReadTotalTimeoutMultiplier; // 讀時(shí)間系數(shù)

            DWORD ReadTotalTimeoutConstant; // 讀時(shí)間常量

            DWORD WriteTotalTimeoutMultiplier; // 寫(xiě)時(shí)間系數(shù)

            DWORD WriteTotalTimeoutConstant; // 寫(xiě)時(shí)間常量

            } COMMTIMEOUTS,*LPCOMMTIMEOUTS;

              COMMTIMEOUTS結(jié)構(gòu)的成員都以毫秒為單位。總超時(shí)的計(jì)算公式是:

            總超時(shí)=時(shí)間系數(shù)×要求讀/寫(xiě)的字符數(shù) + 時(shí)間常量

              例如,如果要讀入10個(gè)字符,那么讀操作的總超時(shí)的計(jì)算公式為:

            讀總超時(shí)=ReadTotalTimeoutMultiplier×10 + ReadTotalTimeoutConstant

              可以看出,間隔超時(shí)和總超時(shí)的設(shè)置是不相關(guān)的,這可以方便通信程序靈活地設(shè)置各種超時(shí)。

            如果所有寫(xiě)超時(shí)參數(shù)均為0,那么就不使用寫(xiě)超時(shí)。如果ReadIntervalTimeout為0,那么就不使用讀間隔超時(shí),如果 ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant都為0,則不使用讀總超時(shí)。如果讀間隔超時(shí)被 設(shè)置成MAXDWORD并且兩個(gè)讀總超時(shí)為0,那么在讀一次輸入緩沖區(qū)中的內(nèi)容后讀操作就立即完成,而不管是否讀入了要求的字符。

              在用重疊方式讀寫(xiě)串行口時(shí),雖然ReadFile和WriteFile在完成操作以前就可能返回,但超時(shí)仍然是起作用的。在這種情況下,超時(shí)規(guī)定的是操作的完成時(shí)間,而不是ReadFile和WriteFile的返回時(shí)間。

            清單12.5列出了一段簡(jiǎn)單的串行口初始化代碼。

             

            清單12.5 打開(kāi)并初始化串行口

            HANDLE hCom;
            DWORD dwError;
            DCB dcb;
            COMMTIMEOUTS TimeOuts;

            hCom
            =CreateFile(“COM2”, // 文件名        
                            GENERIC_READ | GENERIC_WRITE, // 允許讀和寫(xiě)
                            0// 獨(dú)占方式
                            NULL,
                            OPEN_EXISTING, 
            //打開(kāi)而不是創(chuàng)建
                            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // 重疊方式
                            NULL
                            );

            if(hCom = = INVALID_HANDLE_VALUE)
            {
                dwError
            =GetLastError( );    
                . . . 
            // 處理錯(cuò)誤
            }

            SetupComm( hCom, 
            10241024 ) //緩沖區(qū)的大小為1024
            TimeOuts. ReadIntervalTimeout=1000;
            TimeOuts.ReadTotalTimeoutMultiplier
            =500;
            TimeOuts.ReadTotalTimeoutConstant
            =5000;
            TimeOuts.WriteTotalTimeoutMultiplier
            =500;
            TimeOuts.WriteTotalTimeoutConstant
            =5000;
            SetCommTimeouts(hCom, 
            &TimeOuts); // 設(shè)置超時(shí)
            GetCommState(hCom, &dcb);
            dcb.BaudRate
            =2400// 波特率為2400
            dcb.ByteSize=8// 每個(gè)字符有8位
            dcb.Parity=NOPARITY; //無(wú)校驗(yàn)
            dcb.StopBits=ONESTOPBIT; //一個(gè)停止位
            SetCommState(hCom, &dcb);

             

            12.3.3 重疊I/O

            在用ReadFile和WriteFile讀寫(xiě)串行口時(shí),既可以同步執(zhí)行,也可以重疊(異步)執(zhí)行。在同步執(zhí)行時(shí),函數(shù)直到操作完成后才返回。這意味著 在同步執(zhí)行時(shí)線程會(huì)被阻塞,從而導(dǎo)致效率下降。在重疊執(zhí)行時(shí),即使操作還未完成,調(diào)用的函數(shù)也會(huì)立即返回。費(fèi)時(shí)的I/O操作在后臺(tái)進(jìn)行,這樣線程就可以干 別的事情。例如,線程可以在不同的句柄上同時(shí)執(zhí)行I/O操作,甚至可以在同一句柄上同時(shí)進(jìn)行讀寫(xiě)操作。“重疊”一詞的含義就在于此。

              ReadFile函數(shù)只要在串行口輸入緩沖區(qū)中讀入指定數(shù)量的字符,就算完成操作。而WriteFile函數(shù)不但要把指定數(shù)量的字符拷入到輸出緩沖中,而且要等這些字符從串行口送出去后才算完成操作。

            ReadFile和WriteFile函數(shù)是否為執(zhí)行重疊操作是由CreateFile函數(shù)決定的。如果在調(diào)用CreateFile創(chuàng)建句柄時(shí)指定了 FILE_FLAG_OVERLAPPED標(biāo)志,那么調(diào)用ReadFile和WriteFile對(duì)該句柄進(jìn)行的讀寫(xiě)操作就是重疊的,如果未指定重疊標(biāo)志, 則讀寫(xiě)操作是同步的。

              函數(shù)ReadFile和WriteFile的參數(shù)和返回值很相似。這里僅列出ReadFile函數(shù)的聲明:

            BOOL ReadFile(

            HANDLE hFile, // 文件句柄

            LPVOID lpBuffer, // 讀緩沖區(qū)

            DWORD nNumberOfBytesToRead, // 要求讀入的字節(jié)數(shù)

            LPDWORD lpNumberOfBytesRead, // 實(shí)際讀入的字節(jié)數(shù)

            LPOVERLAPPED lpOverlapped // 指向一個(gè)OVERLAPPED結(jié)構(gòu)

            ); //若返回TRUE則表明操作成功

             

            需要注意的是如果該函數(shù)因?yàn)槌瑫r(shí)而返回,那么返回值是TRUE。參數(shù)lpOverlapped在重疊操作時(shí)應(yīng)該指向一個(gè)OVERLAPPED結(jié)構(gòu),如果 該參數(shù)為NULL,那么函數(shù)將進(jìn)行同步操作,而不管句柄是否是由FILE_FLAG_OVERLAPPED標(biāo)志建立的。

            當(dāng)ReadFile和WriteFile返回FALSE時(shí),不一定就是操作失敗,線程應(yīng)該調(diào)用GetLastError函數(shù)分析返回的結(jié)果。例如,在重 疊操作時(shí)如果操作還未完成函數(shù)就返回,那么函數(shù)就返回FALSE,而且GetLastError函數(shù)返回ERROR_IO_PENDING。

            在使用重疊I/O時(shí),線程需要?jiǎng)?chuàng)建OVERLAPPED結(jié)構(gòu)以供讀寫(xiě)函數(shù)使用。OVERLAPPED結(jié)構(gòu)最重要的成員是hEvent,hEvent是一 個(gè)事件對(duì)象句柄,線程應(yīng)該用CreateEvent函數(shù)為hEvent成員創(chuàng)建一個(gè)手工重置事件,hEvent成員將作為線程的同步對(duì)象使用。如果讀寫(xiě)函 數(shù)未完成操作就返回,就那么把hEvent成員設(shè)置成無(wú)信號(hào)的。操作完成后(包括超時(shí)),hEvent會(huì)變成有信號(hào)的。

            如果GetLastError函數(shù)返回ERROR_IO_PENDING,則說(shuō)明重疊操作還為完成,線程可以等待操作完成。有兩種等待辦法:一種辦法是 用象WaitForSingleObject這樣的等待函數(shù)來(lái)等待OVERLAPPED結(jié)構(gòu)的hEvent成員,可以規(guī)定等待的時(shí)間,在等待函數(shù)返回后, 調(diào)用GetOverlappedResult。另一種辦法是調(diào)用GetOverlappedResult函數(shù)等待,如果指定該函數(shù)的bWait參數(shù)為 TRUE,那么該函數(shù)將等待OVERLAPPED結(jié)構(gòu)的hEvent 事件。GetOverlappedResult可以返回一個(gè)OVERLAPPED結(jié)構(gòu)來(lái)報(bào)告包括實(shí)際傳輸字節(jié)在內(nèi)的重疊操作結(jié)果。

            如果規(guī)定了讀/寫(xiě)操作的超時(shí),那么當(dāng)超過(guò)規(guī)定時(shí)間后,hEvent成員會(huì)變成有信號(hào)的。因此,在超時(shí)發(fā)生后,WaitForSingleObject和 GetOverlappedResult都會(huì)結(jié)束等待。WaitForSingleObject的dwMilliseconds參數(shù)會(huì)規(guī)定一個(gè)等待超時(shí), 該函數(shù)實(shí)際等待的時(shí)間是兩個(gè)超時(shí)的最小值。注意GetOverlappedResult不能設(shè)置等待的時(shí)限,因此如果hEvent成員無(wú)信號(hào),則該函數(shù)將 一直等待下去。

              在調(diào)用ReadFile和WriteFile之前,線程應(yīng)該調(diào)用ClearCommError函數(shù)清除錯(cuò)誤標(biāo)志。該函數(shù)負(fù)責(zé)報(bào)告指定的錯(cuò)誤和設(shè)備的當(dāng)前狀態(tài)。

              調(diào)用PurgeComm函數(shù)可以終止正在進(jìn)行的讀寫(xiě)操作,該函數(shù)還會(huì)清除輸入或輸出緩沖區(qū)中的內(nèi)容。

             

            12.3.4 通信事件

            在Windows 95/NT中,WM_COMMNOTIFY消息已經(jīng)取消,在串行口產(chǎn)生一個(gè)通信事件時(shí),程序并不會(huì)收到通知消息。線程需要調(diào)用WaitCommEvent 函數(shù)來(lái)監(jiān)視發(fā)生在串行口中的各種事件,該函數(shù)的第二個(gè)參數(shù)返回一個(gè)事件屏蔽變量,用來(lái)指示事件的類型。線程可以用SetCommMask建立事件屏蔽以指 定要監(jiān)視的事件,表12.4列出了可以監(jiān)視的事件。調(diào)用GetCommMask可以查詢串行口當(dāng)前的事件屏蔽。

             

            表12.4 通信事件

            事件屏蔽

            含義

            EV_BREAK

            檢測(cè)到一個(gè)輸入中斷

            EV_CTS

            CTS信號(hào)發(fā)生變化

            EV_DSR

            DSR信號(hào)發(fā)生變化

            EV_ERR

            發(fā)生行狀態(tài)錯(cuò)誤

            EV_RING

            檢測(cè)到振鈴信號(hào)

            EV_RLSD

            RLSD(CD)信號(hào)發(fā)生變化

            EV_RXCHAR

            輸入緩沖區(qū)接收到新字符

            EV_RXFLAG

            輸入緩沖區(qū)收到事件字符

            EV_TXEMPTY

            發(fā)送緩沖區(qū)為空

            WaitCommEvent即可以同步使用,也可以重疊使用。如果串口是用FILE_FLAG_OVERLAPPED標(biāo)志打開(kāi)的,那么 WaitCommEvent就進(jìn)行重疊操作,此時(shí)該函數(shù)需要一個(gè)OVERLAPPED結(jié)構(gòu)。線程可以調(diào)用等待函數(shù)或 GetOverlappedResult函數(shù)來(lái)等待重疊操作的完成。

              當(dāng)指定 范圍內(nèi)的某一事件發(fā)生后,線程就結(jié)束等待并把該事件的屏蔽碼設(shè)置到事件屏蔽變量中。需要注意的是,WaitCommEvent只檢測(cè)調(diào)用該函數(shù)后發(fā)生的事 件。例如,如果在調(diào)用WaitCommEvent前在輸入緩沖區(qū)中就有字符,則不會(huì)因?yàn)檫@些字符而產(chǎn)生EV_RXCHAR事件。

              如果檢測(cè)到輸入的硬件信號(hào)(如CTS、RTS和CD信號(hào)等)發(fā)生了變化,線程可以調(diào)用GetCommMaskStatus函數(shù)來(lái)查詢它們的狀態(tài)。而用EscapeCommFunction函數(shù)可以控制輸出的硬件信號(hào)(如DTR和RTS信號(hào))。



            原作出處


            posted on 2008-01-07 23:40 isabc 閱讀(2616) 評(píng)論(1)  編輯 收藏 引用 所屬分類: Win32 多線程

            評(píng)論

            # re: 多線程與串行通信 2010-12-17 14:26

            在串口通信時(shí),讀串口時(shí),讀完一幀數(shù)據(jù)就拿去處理,做到實(shí)時(shí)處理。是不是應(yīng)該拿輔助線程來(lái)監(jiān)聽(tīng)讀串口讀完多少字節(jié)在去通知數(shù)據(jù)處理事件來(lái)拿數(shù)據(jù)去處理?  回復(fù)  更多評(píng)論   

            廣告信息(免費(fèi)廣告聯(lián)系)

            中文版MSDN:
            歡迎體驗(yàn)

            99久久免费只有精品国产| 久久久精品无码专区不卡| 国产精品青草久久久久福利99| 国产成人无码精品久久久免费| 久久成人永久免费播放| 一本色道久久综合| 777米奇久久最新地址| 精品熟女少妇aⅴ免费久久| 久久久久久久综合日本| 成人久久免费网站| 精品久久久久久久久久久久久久久| 久久久国产一区二区三区| 久久久久高潮毛片免费全部播放| 久久久久久av无码免费看大片| 久久久无码精品亚洲日韩京东传媒 | 国产精品久久久久久福利69堂| 国产成人综合久久久久久| 久久精品人人槡人妻人人玩AV| AA级片免费看视频久久| 久久精品黄AA片一区二区三区| 久久人人爽人爽人人爽av | 一本久道久久综合狠狠爱| 精品久久久久久无码中文野结衣 | 91精品日韩人妻无码久久不卡| 99久久精品国产一区二区| 久久久久国产亚洲AV麻豆| 青青青青久久精品国产 | 久久精品国产久精国产一老狼| 亚洲精品高清国产一久久| 天堂久久天堂AV色综合| 色综合久久中文字幕无码| 久久夜色精品国产噜噜亚洲a| 久久成人精品| 合区精品久久久中文字幕一区| 日韩亚洲国产综合久久久| 国产精品一区二区久久精品无码| 国产精品对白刺激久久久| 久久99国产综合精品免费| 久久久SS麻豆欧美国产日韩| 欧美精品国产综合久久| 久久精品无码一区二区无码|