<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          關(guān) 閉

          新聞中心

          EEPW首頁(yè) > 工控自動(dòng)化 > 設(shè)計(jì)應(yīng)用 > 用Visual C++實(shí)現(xiàn)工控設(shè)備多線程控制程序

          用Visual C++實(shí)現(xiàn)工控設(shè)備多線程控制程序

          作者: 時(shí)間:2008-04-14 來(lái)源: 收藏

                  提出多線程工控制序中最適宜采用工作者線程和事件同步方式,并給出了一個(gè)通用的工控程序框架。

              多線程技術(shù)的引入,不僅可以挖掘潛在的CPU空閑時(shí)間,而且還可以提高應(yīng)用程序的瓜速度,其優(yōu)點(diǎn)在有多個(gè)任務(wù)需要完成、有巨大數(shù)據(jù)流量的程序中反映得尤為明顯。而隨著Visual C++的引入,其靈活的線程實(shí)現(xiàn)機(jī)制使得程序員從繁瑣的Windows編程中解脫出來(lái)。關(guān)于多線程基本機(jī)理和實(shí)現(xiàn)方法近年來(lái)有許多文章介紹,這里不再贅述。本文將側(cè)重于比較在工控程序中采用各種線程類(lèi)型和同步方法的優(yōu)劣,并給出一個(gè)實(shí)用的、有較廣適應(yīng)性的程序主體框架。

          本文引用地址:http://www.ex-cimer.com/article/81506.htm

            1 各種線程類(lèi)型和同步方法

            1.1 線程類(lèi)型

            Visual C++中線程分為工作者線程(Worker Thread)和用戶界面線程(User Interface Thread)兩大類(lèi)。

            用戶界面線程的特點(diǎn)是擁有單獨(dú)的消息隊(duì)列,可以具有自己的窗口界面,能夠?qū)κ录陀脩糨斎胱龀鲰憫?yīng),具體實(shí)現(xiàn)時(shí)由CwinThread派生出一個(gè)類(lèi)。但其缺點(diǎn)是當(dāng)需要停止或撤銷(xiāo)當(dāng)前正在運(yùn)行的線程而向其發(fā)送中止消息后,只有在消息隊(duì)列中排在前面的消息被一一處理完之后,線程才能接受中止消息并停止當(dāng)前工作,這對(duì)CPU是一種浪費(fèi),在對(duì)實(shí)時(shí)性要求較高的工控程序中是不可容忍的。

            工作者線程選用于處理后臺(tái)任務(wù),而不影響用戶對(duì)應(yīng)用程序的使用。工作者線程僅僅由一個(gè)函數(shù)體實(shí)現(xiàn),其實(shí)現(xiàn)簡(jiǎn)單,便 于編程者控制,與事件同步方法相配合能對(duì)中止消息做出較快反應(yīng)。

            1.2 同步方法

            在多線程應(yīng)用程序中,兩個(gè)或更多的線程同時(shí)訪問(wèn)相同數(shù)據(jù)會(huì)導(dǎo)致不可預(yù)知的結(jié)果,因此保持線程間的同步是一個(gè)不可或缺的環(huán)節(jié)。Visual C++提供了四種同步方法:臨界區(qū)(Critial Section)、信號(hào)燈(Semaphore)、互斥量(Mutex)和事件(Event)。

            其中采用臨界區(qū)、信號(hào)燈或互斥量進(jìn)行同步時(shí),線程間的同步過(guò)程由操作系統(tǒng)完全控制,系統(tǒng)僅僅防止多個(gè)線程對(duì)同一資源的同時(shí)使用,而相同優(yōu)先級(jí)的線程對(duì)同一資源的使用順序是編程者無(wú)法控制的。而在一般工控系統(tǒng)中,當(dāng)主控臺(tái)下方設(shè)備數(shù)據(jù)變化時(shí),應(yīng)能及時(shí)中止當(dāng)前的計(jì)算(如果當(dāng)前計(jì)算未完成的話)并根據(jù)新的數(shù)據(jù)開(kāi)始新一輪的計(jì)算,因而需求各線程對(duì)所處理的數(shù)據(jù)有一定的操作次序。

            事件同步是通過(guò)鈄事件自身設(shè)置為有信號(hào)或無(wú)信號(hào)來(lái)通知其它線程某一操作已完成或尚款完成,其設(shè)置可由編程人員手工完成,適合于工控程序應(yīng)用。盡管事件同步方式平均效率比上面三種方式稍低,但在工控程序應(yīng)用中相對(duì)于因數(shù)據(jù)未能及時(shí)更新而導(dǎo)致大量的無(wú)用計(jì)算及其對(duì)實(shí)時(shí)性的損害來(lái)講,這是非常值得的。

            下面介紹的是筆者參與某‘九五’預(yù)研項(xiàng)目中所設(shè)計(jì)的主控臺(tái)程序的基本框架,這個(gè)程序框架應(yīng)能適用于大多數(shù)工控系統(tǒng)的主控程序。

            2 軟件框架

            一般工控系統(tǒng)的主控部分通常所必須完成的兩件事是:(1)通過(guò)通信端口與下端設(shè)備通信,接收下端設(shè)備傳來(lái)的數(shù)據(jù)或向下端設(shè)備發(fā)送指令;(2)對(duì)下端設(shè)備所傳數(shù)據(jù)進(jìn)行處理。

            與之相對(duì)應(yīng),該軟件具有一個(gè)主線程和兩個(gè)子線程,其中一個(gè)子線程為通信線程,另一個(gè)為計(jì)算線程。主線程是Windows下每個(gè)應(yīng)用程序都具備的,負(fù)責(zé)線程間的同步、向計(jì)算線程和通信線程傳遞參數(shù)、管理人機(jī)界面、接收用戶輸入、數(shù)據(jù)庫(kù)的操作和管理等功能。能信線程通過(guò)通信端口(可以是串口、并口或網(wǎng)絡(luò)接口等)負(fù)責(zé)與下端的設(shè)備進(jìn)行通信并交換數(shù)據(jù),當(dāng)存在多級(jí)控制結(jié)構(gòu)時(shí),還可用來(lái)與更高一級(jí)的控制設(shè)備進(jìn)行通信并向上傳遞數(shù)據(jù)。計(jì)算線程負(fù)責(zé)核心算法的實(shí)現(xiàn),根據(jù)系統(tǒng)的不同完成不同的數(shù)據(jù)處理任務(wù)。程序結(jié)構(gòu)如圖1。

            進(jìn)程開(kāi)始后先由主線程建立通信線程與計(jì)算線程。通信線程監(jiān)視通信端口,當(dāng)下方設(shè)備發(fā)來(lái)數(shù)據(jù)時(shí),就向主線程發(fā)送自定義的WM_USER_COMM_NOTIFY消息,通知主線程計(jì)算數(shù)據(jù)有所改變,主線程則對(duì)之進(jìn)行處理,即中止當(dāng)前的計(jì)算,并重新開(kāi)始計(jì)算。

            3 具體實(shí)現(xiàn)

            用Visual C++r AppWizard生成一個(gè)應(yīng)用程序,這是主控程序的雛形,該應(yīng)用程序暫取名為CrtSys,后面程序名都以此為準(zhǔn)。

            3.1 多線程的定義及生成
            
            3.1.1 多線程的定義

            向項(xiàng)目中加入threads.cpp文件,在該文件中寫(xiě)入通信線程中計(jì)算線程的控制函數(shù)。

            控制函數(shù)有下面的原型:

            UINT MyThreadProc(LPVOID lpvThreadParam);

            lpvThreadParam 參數(shù)是32位的值,這個(gè)值就是在線程對(duì)象產(chǎn)生時(shí)傳遞給線程構(gòu)成造函數(shù)的參數(shù)??刂坪瘮?shù)能解釋此值的不同表現(xiàn)方式。它可以被當(dāng)作一個(gè)普通變量對(duì)待,也可以被視為一個(gè)指向包含有多個(gè)參數(shù)的結(jié)構(gòu)指針,也可以被忽略。如果參數(shù)指向一個(gè)結(jié)構(gòu),這個(gè)結(jié)構(gòu)可能不僅僅用來(lái)從調(diào)用者傳遞參數(shù)給線程,還可能用來(lái)從線程回傳數(shù)據(jù)給調(diào)用者。如果使用這樣的結(jié)構(gòu)回傳給調(diào)用者,結(jié)果準(zhǔn)備好后線程需要通知調(diào)用者。

            控制函數(shù)終止時(shí),應(yīng)該返回一個(gè)UINT類(lèi)型的值,表明終止的原因。返回碼0表示成功,其它值表示不同類(lèi)型的錯(cuò)誤,這完全依賴實(shí)現(xiàn)情況。

            按一般程序示例,線程通常在視類(lèi)或框架窗口類(lèi)中產(chǎn)生。但在工控程序中,通信與計(jì)算線程常常要大量地對(duì)計(jì)算數(shù)據(jù)進(jìn)行操作,根據(jù)文檔/視的程序框架結(jié)構(gòu),文檔類(lèi)常常用存儲(chǔ)所要處理的數(shù)據(jù)。因此把計(jì)算與通信線程放在文檔類(lèi)中產(chǎn)生,并把產(chǎn)生線程的當(dāng)前文檔對(duì)象的指針作為線程控制函數(shù)的參數(shù)傳遞給線程。

            從而,在控制函數(shù)(CalcThreadProc()和CommThreadProc())一開(kāi)始,就要對(duì)所傳來(lái)的參數(shù)進(jìn)行識(shí)別:

            CctrsysDoc* pDoc=(CctrsysDoc*)pParam;
             注意要在文件開(kāi)頭包括進(jìn)文檔類(lèi)的頭文件
            #include"CtrsysDoc.h"

            3.1.2 多線程的產(chǎn)生

            在文檔類(lèi)的構(gòu)造函數(shù)中產(chǎn)生線程。程序啟動(dòng)時(shí)生成文檔對(duì)象,同時(shí)啟動(dòng)兩個(gè)線程。

            ///////////////////////////////////
            // CctrsysDoc construction/destruction
            CctrsysDoc::CctrsysDoc()
            {
            ……
            m_pCalcThread=AfxBeginThread(CalcThreadProc,this);
            m_pCommThread=AfxBeginThread (CommThreadPros,this);
            }

            注意不要用Win32的CreateThread()建立線程,而應(yīng)該用AfxBeginThread()函數(shù),否則所建立的線程不能訪問(wèn)其它MFC對(duì)象。

            3.2 線程間的同步

            程序中設(shè)置有八個(gè)事件用于線程同步:

            HANDLE m_hEventPost;//用來(lái)允許通信線程向主框架發(fā)送WM_USER_COMM_NOTIFY消息
            HANDLE m_hEventStartCalc;//主框架通知計(jì)算線程開(kāi)始計(jì)算
            HANDLE m_hEventCalcStarted;//計(jì)算線程通知主框架計(jì)算已經(jīng)開(kāi)始
            HANDLE m-hEventStopCalc;//主框架通知計(jì)算線程中止計(jì)算
            HANDLE m-hEventCalcStopped;//計(jì)算線程通知主框架計(jì)算已經(jīng)中止
            HANDLE m-hEventCalcDone;//計(jì)算線程通知主框架計(jì)算已經(jīng)結(jié)束
            HANDLE m-hEventUpdateSourceData;//主框架通知計(jì)算線程更新數(shù)據(jù)
            HANDLE m-hEventSourceDataUpdated;//能信線程通知主框架數(shù)據(jù)已更新完畢

            這八個(gè)事件是主線程和兩個(gè)子線程之間同步所必需的,讀者可根據(jù)自己程序的需要另外添加。

            因各線程都以文檔對(duì)象指針為參數(shù),這些事件都在文檔類(lèi)頭文件中定義,這引起事件在文檔類(lèi)的構(gòu)造函數(shù)中生成并賦初值。

            CctrsysDoc::CctrsysDoc()
            {
            ……
            m_hEventPost =CreateEvent (NULL,TRUE,TRUE,NULL);
            m_hEventCalcDone=CreateEvent (NULL,TRUE,FALSE,NULL);
            m_hEventCalcStarted=CreateEvent(NULL,TRUE,FALSE,NULL);
            m_hEventStartCalc=CreateEvent(NULL,TRUE,FALSE,NULL);
            m_hEventSourceDataUpdated=CreateEvent(NULL,TRUE,FALSE,NULL);
            m_hEventUpdateSourceData=CreateEvent(NULL,TRUE,FALSE,NULL);
            m_hEventCalcStopped =CreateEvent (NULL,TRUE,FALSE,NULL);
            m_hEventStopCalc=CreateEvent(NULL,TRUE,FALSE,NULL);
            ……
            }

            線程的同步工作主要在主框架CmainFrame類(lèi)的WM_USER_COMM_NOTIFY消息響應(yīng)函數(shù)OnCommNotify中進(jìn)行。當(dāng)下方通信設(shè)備參數(shù)改變時(shí),通信線程發(fā)送給CmainFrame類(lèi)一個(gè)WM_USER_COMM_NOTIFY消息。CmainFrame類(lèi)接收到消息后,在消息響應(yīng)函數(shù)OnCommNotify中終止計(jì)算線程的當(dāng)前計(jì)算,計(jì)算成功終止后由能信線程更新計(jì)算所需的數(shù)據(jù)源,待更新完畢后,重新開(kāi)始計(jì)算。線程同步部分流程如圖2。

            3.3 通信線程
           
            通信線程部分流程如圖3所示。

            3.4 計(jì)算線程

            編程者應(yīng)根據(jù)數(shù)據(jù)處理過(guò)程,在運(yùn)算量較大或循環(huán)次數(shù)較多的地方設(shè)置對(duì)m_hEventStopCalc事件的查詢。當(dāng)數(shù)據(jù)發(fā)生更新時(shí),使用其它線程類(lèi)型和同步方法往往必須等到數(shù)據(jù)處理部分結(jié)束,這樣整個(gè)一次數(shù)據(jù)處理都是無(wú)用計(jì)算;而采用上述方法,因數(shù)據(jù)更新所造成的無(wú)用計(jì)算僅僅是一步循環(huán)或幾行指令,相比而言,所導(dǎo)致的延時(shí)和CPU浪費(fèi)是微不足道的。

            計(jì)算線程部分流程如圖4所示。


          c++相關(guān)文章:c++教程




          關(guān)鍵詞: Visual C++ 多線程

          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();