日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

用VC編寫基于Windows的精確定時(shí)程序

 Delores 2007-11-15

    在工業(yè)生產(chǎn)控制系統(tǒng)中,有許多需要定時(shí)完成的操作,如定時(shí)顯示當(dāng)前時(shí)間,定時(shí)刷新屏幕上的進(jìn)度條,上位 機(jī)定時(shí)向下位機(jī)發(fā)送命令和傳送數(shù)據(jù)等。特別是在對控制性能要求較高的實(shí)時(shí)控制系統(tǒng)和數(shù)據(jù)采集系統(tǒng)中,就更需要精確定時(shí)操作。

     

    眾所周知,Windows 是基于消息機(jī)制的系統(tǒng),任何事件的執(zhí)行都是通過發(fā)送和接收消息來完成的。 這樣就帶來了一些問題,如一旦計(jì)算機(jī)的CPU被某個(gè)進(jìn)程占用,或系統(tǒng)資源緊張時(shí),發(fā)送到消息隊(duì)列 中的消息就暫時(shí)被掛起,得不到實(shí)時(shí)處理。因此,不能簡單地通過Windows消息引發(fā)一個(gè)對定時(shí)要求 嚴(yán)格的事件。另外,由于在Windows中已經(jīng)封裝了計(jì)算機(jī)底層硬件的訪問,所以,要想通過直接利用 訪問硬件來完成精確定時(shí),也比較困難。所以在實(shí)際應(yīng)用時(shí),應(yīng)針對具體定時(shí)精度的要求,采取相適 應(yīng)的定時(shí)方法。

     

    VC中提供了很多關(guān)于時(shí)間操作的函數(shù),利用它們控制程序能夠精確地完成定時(shí)和計(jì)時(shí)操作。本文詳細(xì)介紹了 VC中基于Windows的精確定時(shí)的七種方式,如下圖所示:

     

     

    方式一:VC中的WM_TIMER消息映射能進(jìn)行簡單的時(shí)間控制。首先調(diào)用函數(shù)SetTimer()設(shè)置定時(shí) 間隔,如SetTimer(0,200,NULL)即為設(shè)置200ms的時(shí)間間隔。然后在應(yīng)用程序中增加定時(shí)響應(yīng)函數(shù) OnTimer(),并在該函數(shù)中添加響應(yīng)的處理語句,用來完成到達(dá)定時(shí)時(shí)間的操作。這種定時(shí)方法非常 簡單,可以實(shí)現(xiàn)一定的定時(shí)功能,但其定時(shí)功能如同Sleep()函數(shù)的延時(shí)功能一樣,精度非常低,最小 計(jì)時(shí)精度僅為30ms,CPU占用低,且定時(shí)器消息在多任務(wù)操作系統(tǒng)中的優(yōu)先級很低,不能得到及時(shí)響 應(yīng),往往不能滿足實(shí)時(shí)控制環(huán)境下的應(yīng)用。只可以用來實(shí)現(xiàn)諸如位圖的動(dòng)態(tài)顯示等對定時(shí)精度要求不高的情況。如示例工程中的Timer1。

     

    方式二:VC中使用sleep()函數(shù)實(shí)現(xiàn)延時(shí),它的單位是ms,如延時(shí)2秒,用sleep(2000)。精度非常 低,最小計(jì)時(shí)精度僅為30ms,用sleep函數(shù)的不利處在于延時(shí)期間不能處理其他的消息,如果時(shí)間太 長,就好象死機(jī)一樣,CPU占用率非常高,只能用于要求不高的延時(shí)程序中。如示例工程中的Timer2。

     

    方式三:利用COleDateTime類和COleDateTimeSpan類結(jié)合WINDOWS的消息處理過程來實(shí)現(xiàn)秒級延時(shí)。如示例工程中的Timer3和Timer3_1。以下是實(shí)現(xiàn)2秒的延時(shí)代碼:

    COleDateTime start_time = COleDateTime::GetCurrentTime();COleDateTimeSpan end_time= COleDateTime::GetCurrentTime()-start_time;while(end_time.GetTotalSeconds()< 2) //實(shí)現(xiàn)延時(shí)2秒{ MSG msg;GetMessage(&msg,NULL,0,0);TranslateMessage(&msg); DispatchMessage(&msg);//以上四行是實(shí)現(xiàn)在延時(shí)或定時(shí)期間能處理其他的消息, //雖然這樣可以降低CPU的占有率,//但降低了延時(shí)或定時(shí)精度,實(shí)際應(yīng)用中可以去掉。end_time = COleDateTime::GetCurrentTime()-start_time;}//這樣在延時(shí)的時(shí)候我們也能夠處理其他的消息。

     

    方式四:在精度要求較高的情況下,VC中可以利用GetTickCount()函數(shù),該函數(shù)的返回值是 DWORD型,表示以ms為單位的計(jì)算機(jī)啟動(dòng)后經(jīng)歷的時(shí)間間隔。精度比WM_TIMER消息映射高,在較 短的定時(shí)中其計(jì)時(shí)誤差為15ms,在較長的定時(shí)中其計(jì)時(shí)誤差較低,如果定時(shí)時(shí)間太長,就好象死機(jī)一樣,CPU占用率非常高,只能用于要求不高的延時(shí)程序中。如示例工程中的Timer4和Timer4_1。下列代碼可以實(shí)現(xiàn)50ms的精確定時(shí):

    DWORD dwStart = GetTickCount();DWORD dwEnd = dwStart;do{ dwEnd = GetTickCount()-dwStart;}while(dwEnd <50);

     

    為使GetTickCount()函數(shù)在延時(shí)或定時(shí)期間能處理其他的消息,可以把代碼改為:

    DWORD dwStart = GetTickCount();DWORD dwEnd = dwStart;do{ MSG msg; GetMessage(&msg,NULL,0,0); TranslateMessage(&msg); DispatchMessage(&msg); dwEnd = GetTickCount()-dwStart;}while(dwEnd <50);

     

    雖然這樣可以降低CPU的占有率,并在延時(shí)或定時(shí)期間也能處理其他的消息,但降低了延時(shí)或定時(shí)精度。

     

    方式五:與GetTickCount()函數(shù)類似的多媒體定時(shí)器函數(shù)DWORD timeGetTime(void),該函數(shù)定時(shí)精 度為ms級,返回從Windows啟動(dòng)開始經(jīng)過的毫秒數(shù)。微軟公司在其多媒體Windows中提供了精確定時(shí)器的底 層API持,利用多媒體定時(shí)器可以很精確地讀出系統(tǒng)的當(dāng)前時(shí)間,并且能在非常精確的時(shí)間間隔內(nèi)完成一 個(gè)事件、函數(shù)或過程的調(diào)用。不同之處在于調(diào)用DWORD timeGetTime(void) 函數(shù)之前必須將 Winmm.lib 和 Mmsystem.h 添加到工程中,否則在編譯時(shí)提示DWORD timeGetTime(void)函數(shù)未定義。由于使用該 函數(shù)是通過查詢的方式進(jìn)行定時(shí)控制的,所以,應(yīng)該建立定時(shí)循環(huán)來進(jìn)行定時(shí)事件的控制。如示例工程中的Timer5和Timer5_1。

     

     

     

     

     

     

    方式六:使用多媒體定時(shí)器timeSetEvent()函數(shù),該函數(shù)定時(shí)精度為ms級。利用該函數(shù)可以實(shí)現(xiàn)周期性的函數(shù)調(diào)用。如示例工程中的Timer6和Timer6_1。函數(shù)的原型如下:

    MMRESULT timeSetEvent( UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, WORD dwUser, UINT fuEvent )

     

    該函數(shù)設(shè)置一個(gè)定時(shí)回調(diào)事件,此事件可以是一個(gè)一次性事件或周期性事件。事件一旦被激活,便調(diào)用指定的回調(diào)函數(shù), 成功后返回事件的標(biāo)識符代碼,否則返回NULL。函數(shù)的參數(shù)說明如下:

     

    uDelay:以毫秒指定事件的周期。

    Uresolution:以毫秒指定延時(shí)的精度,數(shù)值越小定時(shí)器事件分辨率越高。缺省值為1ms。

    LpTimeProc:指向一個(gè)回調(diào)函數(shù)。

    DwUser:存放用戶提供的回調(diào)數(shù)據(jù)。

    FuEvent:指定定時(shí)器事件類型:

    TIME_ONESHOT:uDelay毫秒后只產(chǎn)生一次事件

    TIME_PERIODIC :每隔uDelay毫秒周期性地產(chǎn)生事件。

     

    具體應(yīng)用時(shí),可以通過調(diào)用timeSetEvent()函數(shù),將需要周期性執(zhí)行的任務(wù)定義在LpTimeProc回調(diào)函數(shù) 中(如:定時(shí)采樣、控制等),從而完成所需處理的事件。需要注意的是,任務(wù)處理的時(shí)間不能大于周期間隔時(shí)間。另外,在定時(shí)器使用完畢后, 應(yīng)及時(shí)調(diào)用timeKillEvent()將之釋放。

     

    方式七:對于精確度要求更高的定時(shí)操作,則應(yīng)該使用QueryPerformanceFrequency()和 QueryPerformanceCounter()函數(shù)。這兩個(gè)函數(shù)是VC提供的僅供Windows 95及其后續(xù)版本使用的精確時(shí)間函數(shù),并要求計(jì)算機(jī)從硬件上支持精確定時(shí)器。如示例工程中的Timer7、Timer7_1、Timer7_2、Timer7_3。

     

    QueryPerformanceFrequency()函數(shù)和QueryPerformanceCounter()函數(shù)的原型如下:

    BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);BOOL QueryPerformanceCounter(LARGE_INTEGER *lpCount);

     

    數(shù)據(jù)類型ARGE_INTEGER既可以是一個(gè)8字節(jié)長的整型數(shù),也可以是兩個(gè)4字節(jié)長的整型數(shù)的聯(lián)合結(jié)構(gòu), 其具體用法根據(jù)編譯器是否支持64位而定。該類型的定義如下:

    typedef union _LARGE_INTEGER{ struct {  DWORD LowPart ;// 4字節(jié)整型數(shù)  LONG HighPart;// 4字節(jié)整型數(shù) }; LONGLONG QuadPart ;// 8字節(jié)整型數(shù)}LARGE_INTEGER ;

     

    在進(jìn)行定時(shí)之前,先調(diào)用QueryPerformanceFrequency()函數(shù)獲得機(jī)器內(nèi)部定時(shí)器的時(shí)鐘頻率, 然后在需要嚴(yán)格定時(shí)的事件發(fā)生之前和發(fā)生之后分別調(diào)用QueryPerformanceCounter()函數(shù),利用兩次獲得的計(jì)數(shù)之差及時(shí)鐘頻率,計(jì)算出事件經(jīng) 歷的精確時(shí)間。下列代碼實(shí)現(xiàn)1ms的精確定時(shí):

    LARGE_INTEGER litmp; LONGLONG QPart1,QPart2;double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(&litmp);dfFreq = (double)litmp.QuadPart;// 獲得計(jì)數(shù)器的時(shí)鐘頻率QueryPerformanceCounter(&litmp);QPart1 = litmp.QuadPart;// 獲得初始值do{ QueryPerformanceCounter(&litmp); QPart2 = litmp.QuadPart;//獲得中止值 dfMinus = (double)(QPart2-QPart1); dfTim = dfMinus / dfFreq;// 獲得對應(yīng)的時(shí)間值,單位為秒}while(dfTim<0.001);

     

    其定時(shí)誤差不超過1微秒,精度與CPU等機(jī)器配置有關(guān)。 下面的程序用來測試函數(shù)Sleep(100)的精確持續(xù)時(shí)間:

    LARGE_INTEGER litmp; LONGLONG QPart1,QPart2;double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(&litmp);dfFreq = (double)litmp.QuadPart;// 獲得計(jì)數(shù)器的時(shí)鐘頻率QueryPerformanceCounter(&litmp);QPart1 = litmp.QuadPart;// 獲得初始值Sleep(100);QueryPerformanceCounter(&litmp);QPart2 = litmp.QuadPart;//獲得中止值dfMinus = (double)(QPart2-QPart1);dfTim = dfMinus / dfFreq;// 獲得對應(yīng)的時(shí)間值,單位為秒

     

    由于Sleep()函數(shù)自身的誤差,上述程序每次執(zhí)行的結(jié)果都會有微小誤差。下列代碼實(shí)現(xiàn)1微秒的精確定時(shí):

    LARGE_INTEGER litmp; LONGLONG QPart1,QPart2;double dfMinus, dfFreq, dfTim; QueryPerformanceFrequency(&litmp);dfFreq = (double)litmp.QuadPart;// 獲得計(jì)數(shù)器的時(shí)鐘頻率QueryPerformanceCounter(&litmp);QPart1 = litmp.QuadPart;// 獲得初始值do{ QueryPerformanceCounter(&litmp); QPart2 = litmp.QuadPart;//獲得中止值 dfMinus = (double)(QPart2-QPart1); dfTim = dfMinus / dfFreq;// 獲得對應(yīng)的時(shí)間值,單位為秒}while(dfTim<0.000001);

     

    其定時(shí)誤差一般不超過0.5微秒,精度與CPU等機(jī)器配置有關(guān)。

    共2頁。 9 7 1 2

     

     

     

     

     

     

     

     

     

     

     

     

    使用CPU時(shí)間戳進(jìn)行高精度計(jì)時(shí)   

     

    從一論壇看到的轉(zhuǎn)貼,不知道作者名?! ﹃P(guān)注性能的程序開發(fā)人員而言,一個(gè)好的計(jì)時(shí)部件既是益友,也是良師。計(jì)時(shí)器既可以作為程序組件幫助程序員精確的控制程序進(jìn)程,又是一件有力的調(diào)試武器,在有經(jīng)驗(yàn)的程序員手里可以盡快的確定程序的性能瓶頸,或者對不同的算法作出有說服力的性能比較?! ≡赪indows平臺下,常用的計(jì)時(shí)器有兩種,一種是timeGetTime多媒體計(jì)時(shí)器,它可以提供毫秒級的計(jì)時(shí)。但這個(gè)精度對很多應(yīng)用場合而言還是太粗糙了。另一種是QueryPerformanceCount計(jì)數(shù)器,隨系統(tǒng)的不同可以提供微秒級的計(jì)數(shù)。對于實(shí)時(shí)圖形處理、多媒體數(shù)據(jù)流處理、或者實(shí)時(shí)系統(tǒng)構(gòu)造的程序員,善用QueryPerformanceCount/QueryPerformanceFrequency是一項(xiàng)基本功?! ”疚囊榻B的,是另一種直接利用Pentium CPU內(nèi)部時(shí)間戳進(jìn)行計(jì)時(shí)的高精度計(jì)時(shí)手段。以下討論主要得益于《Windows圖形編程》一書,第15頁-17頁,有興趣的讀者可以直接參考該書。關(guān)于RDTSC指令的詳細(xì)討論,可以參考Intel產(chǎn)品手冊。本文僅僅作拋磚之用?! ≡贗ntel Pentium以上級別的CPU中,有一個(gè)稱為“時(shí)間戳(Time Stamp)”的部件,它以64位無符號整型數(shù)的格式,記錄了自CPU上電以來所經(jīng)過的時(shí)鐘周期數(shù)。由于目前的CPU主頻都非常高,因此這個(gè)部件可以達(dá)到納秒級的計(jì)時(shí)精度。這個(gè)精確性是上述兩種方法所無法比擬的?! ≡赑entium以上的CPU中,提供了一條機(jī)器指令RDTSC(Read Time Stamp Counter)來讀取這個(gè)時(shí)間戳的數(shù)字,并將其保存在EDX:EAX寄存器對中。由于EDX:EAX寄存器對恰好是Win32平臺下C++語言保存函數(shù)返回值的寄存器,所以我們可以把這條指令看成是一個(gè)普通的函數(shù)調(diào)用。像這樣:inline unsigned __int64 GetCycleCount(){__asm RDTSC}

     

     

     

     

     

     

     

     

     

     

     

     

     

    但是不行,因?yàn)镽DTSC不被C++的內(nèi)嵌匯編器直接支持,所以我們要用_emit偽指令直接嵌入該指令的機(jī)器碼形式0X0F、0X31,如下:

     

    inline unsigned __int64 GetCycleCount()

    {

    __asm _emit 0x0F

    __asm _emit 0x31

    }

     

    以后在需要計(jì)數(shù)器的場合,可以像使用普通的Win32 API一樣,調(diào)用兩次GetCycleCount函數(shù),比較兩個(gè)返回值的差,像這樣:

     

    unsigned long t;

    t = (unsigned long)GetCycleCount();

    //Do Something time-intensive ...

    t -= (unsigned long)GetCycleCount();

     

    《Windows圖形編程》第15頁編寫了一個(gè)類,把這個(gè)計(jì)數(shù)器封裝起來。有興趣的讀者可以去參考那個(gè)類的代碼。作者為了更精確的定時(shí),做了一點(diǎn)小小的改進(jìn),把執(zhí)行RDTSC指令的時(shí)間,通過連續(xù)兩次調(diào)用GetCycleCount函數(shù)計(jì)算出來并保存了起來,以后每次計(jì)時(shí)結(jié)束后,都從實(shí)際得到的計(jì)數(shù)中減掉這一小段時(shí)間,以得到更準(zhǔn)確的計(jì)時(shí)數(shù)字。但我個(gè)人覺得這一點(diǎn)點(diǎn)改進(jìn)意義不大。在我的機(jī)器上實(shí)測,這條指令大概花掉了幾十到100多個(gè)周期,在Celeron 800MHz的機(jī)器上,這不過是十分之一微秒的時(shí)間。對大多數(shù)應(yīng)用來說,這點(diǎn)時(shí)間完全可以忽略不計(jì);而對那些確實(shí)要精確到納秒數(shù)量級的應(yīng)用來說,這個(gè)補(bǔ)償也過于粗糙了。

    這個(gè)方法的優(yōu)點(diǎn)是:

    1. 高精度??梢灾苯舆_(dá)到納秒級的計(jì)時(shí)精度(在1GHz的CPU上每個(gè)時(shí)鐘周期就是一納秒),這是其他計(jì)時(shí)方法所難以企及的。

     

    2.成本低。timeGetTime 函數(shù)需要鏈接多媒體庫winmm.lib,QueryPerformance* 函數(shù)根據(jù)MSDN的說明,需要硬件的支持(雖然我還沒有見過不支持的機(jī)器)和KERNEL庫的支持,所以二者都只能在Windows平臺下使用(關(guān)于DOS平臺下的高精度計(jì)時(shí)問題,可以參考《圖形程序開發(fā)人員指南》,里面有關(guān)于控制定時(shí)器8253的詳細(xì)說明)。但RDTSC指令是一條CPU指令,凡是i386平臺下Pentium以上的機(jī)器均支持,甚至沒有平臺的限制(我相信i386版本UNIX和Linux下這個(gè)方法同樣適用,但沒有條件試驗(yàn)),而且函數(shù)調(diào)用的開銷是最小的。

     

    3.具有和CPU主頻直接對應(yīng)的速率關(guān)系。一個(gè)計(jì)數(shù)相當(dāng)于1/(CPU主頻Hz數(shù))秒,這樣只要知道了CPU的主頻,可以直接計(jì)算出時(shí)間。這和QueryPerformanceCount不同,后者需要通過QueryPerformanceFrequency獲取當(dāng)前計(jì)數(shù)器每秒的計(jì)數(shù)次數(shù)才能換算成時(shí)間。

    這個(gè)方法的缺點(diǎn)是:

     

    1.現(xiàn)有的C/C++編譯器多數(shù)不直接支持使用RDTSC指令,需要用直接嵌入機(jī)器碼的方式編程,比較麻煩。

     

    2. 數(shù)據(jù)抖動(dòng)比較厲害。其實(shí)對任何計(jì)量手段而言,精度和穩(wěn)定性永遠(yuǎn)是一對矛盾。如果用低精度的timeGetTime來計(jì)時(shí),基本上每次計(jì)時(shí)的結(jié)果都是相同的;而RDTSC指令每次結(jié)果都不一樣,經(jīng)常有幾百甚至上千的差距。這是這種方法高精度本身固有的矛盾。

     

    關(guān)于這個(gè)方法計(jì)時(shí)的最大長度,我們可以簡單的用下列公式計(jì)算:

    自CPU上電以來的秒數(shù) = RDTSC讀出的周期數(shù) / CPU主頻速率(Hz)

    64位無符號整數(shù)所能表達(dá)的最大數(shù)字是1.8×10^19,在我的Celeron 800上可以計(jì)時(shí)大約700年(書中說可以在200MHzPentium上計(jì)時(shí)117年,這個(gè)數(shù)字不知道是怎么得出來的,與我的計(jì)算有出入)。無論如何,我們大可不必關(guān)心溢出的問題。

    下面是幾個(gè)小例子,簡要比較了三種計(jì)時(shí)方法的用法與精度

     

    //Timer1.cpp 使用了RDTSC指令的Timer類//KTimer類的定義可以參見《Windows圖形編程》P15

    //編譯行:CL Timer1.cpp /link USER32.lib

    #include <stdio.h>

    #include "KTimer.h"

     

    main()

    {

      unsigned t;

      KTimer timer;

      timer.Start();

      Sleep(1000);

      t = timer.Stop();

      printf("Lasting Time: %d\n",t);

    }

     

    //Timer2.cpp 使用了timeGetTime函數(shù)

    //需包含<mmsys.h>,但由于Windows頭文件錯(cuò)綜復(fù)雜的關(guān)系

    //簡單包含<windows.h>比較偷懶:)

    //編譯行:CL timer2.cpp /link winmm.lib

    #include <windows.h>

    #include <stdio.h>

     

    main()

    {

      DWORD t1, t2;

      t1 = timeGetTime();

      Sleep(1000);

      t2 = timeGetTime();

      printf("Begin Time: %u\n", t1);

      printf("End Time: %u\n", t2);

      printf("Lasting Time: %u\n",(t2-t1));

    }

     

    //Timer3.cpp 使用了QueryPerformanceCounter函數(shù)

    //編譯行:CL timer3.cpp /link KERNEl32.lib

    #include <windows.h>

    #include <stdio.h>

     

    main()

    {

      LARGE_INTEGER t1, t2, tc;

      QueryPerformanceFrequency(&tc);

      printf("Frequency: %u\n", tc.QuadPart);

      QueryPerformanceCounter(&t1);

      Sleep(1000);

      QueryPerformanceCounter(&t2);

      printf("Begin Time: %u\n", t1.QuadPart);

      printf("End Time: %u\n", t2.QuadPart);

      printf("Lasting Time: %u\n",( t2.QuadPart- t1.QuadPart));

    }

    ////////////////////////////////////////////////

    //以上三個(gè)示例程序都是測試1秒鐘休眠所耗費(fèi)的時(shí)間

    file://測/試環(huán)境:Celeron 800MHz / 256M SDRAM

    // Windows 2000 Professional SP2

    // Microsoft Visual C++ 6.0 SP5

    ////////////////////////////////////////////////

     

    以下是Timer1的運(yùn)行結(jié)果,使用的是高精度的RDTSC指令

    Lasting Time: 804586872

     

    以下是Timer2的運(yùn)行結(jié)果,使用的是最粗糙的timeGetTime API

    Begin Time: 20254254

    End Time: 20255255

    Lasting Time: 1001

     

    以下是Timer3的運(yùn)行結(jié)果,使用的是QueryPerformanceCount API

    Frequency: 3579545

    Begin Time: 3804729124

    End Time: 3808298836

    Lasting Time: 3569712

     

    對于wince, 只有采用KTimer那種方式,因?yàn)镼ueryPerformanceFrequency,timeGetTime均不支持。

     

     

     

    Visual C++實(shí)現(xiàn)微秒級精度定時(shí)器

    所屬類別:VC++

    推薦指數(shù):★★★☆

    文檔人氣:1117

    本周人氣:10

      發(fā)布日期:2006-6-25

    在工業(yè)生產(chǎn)控制系統(tǒng)中,有許多需要定時(shí)完成的操作,如:定時(shí)顯示當(dāng)前時(shí)間,定時(shí)刷新屏幕上的進(jìn)度條,上位機(jī)定時(shí)向下位機(jī)發(fā)送命令和傳送數(shù)據(jù)等。特別是在對控制性能要求較高的控制系統(tǒng)和數(shù)據(jù)采集系統(tǒng)中,就更需要精確定時(shí)操作。眾所周知,Windows是基于消息機(jī)制的系統(tǒng),任何事件的執(zhí)行都是通過發(fā)送和接收消息來完成的。這樣就帶來了一些問題,如一旦計(jì)算機(jī)的CPU被某個(gè)進(jìn)程占用,或系統(tǒng)資源緊張時(shí),發(fā)送到消息隊(duì)列中的消息就暫時(shí)被掛起,得不到實(shí)時(shí)處理。因此,不能簡單地通過Windows消息引發(fā)一個(gè)對定時(shí)要求嚴(yán)格的事件。另外,由于在Windows中已經(jīng)封裝了計(jì)算機(jī)底層硬件的訪問,所以要想通過直接利用訪問硬件來完成精確定時(shí),也比較困難。在實(shí)際應(yīng)用時(shí),應(yīng)針對具體定時(shí)精度的要求,采取與之相適應(yīng)的定時(shí)方法。

     

    本實(shí)例實(shí)現(xiàn)了一中微秒級的精確定時(shí),程序的界面提供了兩個(gè)"Edit"編輯框,其中一個(gè)編輯框輸入用戶理想的定時(shí)長度,另外一個(gè)編輯框返回實(shí)際的時(shí)間長度,經(jīng)過大量的實(shí)驗(yàn)測試,一般情況下誤差不超過5個(gè)微秒。程序的運(yùn)行界面如圖一所示:

    圖一、實(shí)現(xiàn)微秒級的精確定時(shí)器

     

    一、實(shí)現(xiàn)方法

     

    Visual C++中提供了很多關(guān)于時(shí)間操作的函數(shù),利用它們控制程序能夠精確地完成定時(shí)和計(jì)時(shí)操作。Visaul C++中的WM_TIMER消息映射能進(jìn)行簡單的時(shí)間控制。首先調(diào)用函數(shù)SetTimer()設(shè)置定時(shí)間隔(退出程序時(shí)別忘了調(diào)用和SetTimer()配對使用的KillTimer()函數(shù)),如SetTimer(0,200,NULL)即為設(shè)置200ms的時(shí)間間隔。然后在應(yīng)用程序中增加定時(shí)響應(yīng)函數(shù)OnTimer(),并在該函數(shù)中添加響應(yīng)的處理語句,用來完成到達(dá)定時(shí)時(shí)間的操作。這種定時(shí)方法非常簡單,但其定時(shí)功能如同Sleep()函數(shù)的延時(shí)功能一樣,精度非常低,只可以用來實(shí)現(xiàn)諸如位圖的動(dòng)態(tài)顯示等對定時(shí)精度要求不高的情況。

     

    微軟公司在其多媒體Windows中提供了精確定時(shí)器的底層API支持。利用多媒體定時(shí)器可以很精確地讀出系統(tǒng)的當(dāng)前時(shí)間,并且能在非常精確的時(shí)間間隔內(nèi)完成一個(gè)事件、函數(shù)或過程的調(diào)用。利用多媒體定時(shí)器的基本功能,可以通過兩種方法實(shí)現(xiàn)精確定時(shí)。1)使用timeGetTime()函數(shù),該函數(shù)定時(shí)精度為ms級,返回從Windows啟動(dòng)開始所經(jīng)過的時(shí)間。由于使用該函數(shù)是通過查詢的方式進(jìn)行定時(shí)控制的,所以,應(yīng)該建立定時(shí)循環(huán)來進(jìn)行定時(shí)事件的控制。2)使用timeSetEvent()函數(shù),該函數(shù)原型如下:

    MMRESULT timeSetEvent(UINT uDelay,UINT uResolution,LPTIMECALLBACK lpTimeProc,DWORD dwUser,UINT fuEvent);

     

    該函數(shù)的參數(shù)說明如下:參數(shù)uDelay表示延遲時(shí)間;參數(shù)uResolution表示時(shí)間精度,在Windows中缺省值為1ms;lpTimeProc表示回調(diào)函數(shù),為用戶自定義函數(shù),定時(shí)調(diào)用; 參數(shù)dwUser表示用戶提供的回調(diào)數(shù)據(jù);參數(shù)fuEvent為定時(shí)器的事件類型,TIME_ONESHOT表示執(zhí)行一次;TIME_PERIODIC:周期性執(zhí)行。具體應(yīng)用時(shí),可以通過調(diào)用timeSetEvent()函數(shù),將需要周期性執(zhí)行的任務(wù)定義在lpTimeProc回調(diào)函數(shù)中(如:定時(shí)采樣、控制等),從而完成所需處理的事件。需要注意的是:任務(wù)處理的時(shí)間不能大于周期間隔時(shí)間。另外,在定時(shí)器使用完畢后,應(yīng)及時(shí)調(diào)用timeKillEvent()將之釋放。下面這段代碼的主要功能是設(shè)置兩個(gè)時(shí)鐘定時(shí)器,一個(gè)間隔是1ms,一個(gè)間隔是2s。每執(zhí)行一次,把當(dāng)前系統(tǒng)時(shí)鐘值輸入文件"cure.out"中,以比較該定時(shí)器的精確度。

    # define ONE_MILLI_SECOND 1 //定義1ms和2s時(shí)鐘間隔,以ms為單位 ;# define TWO_SECOND 2000 # define TIMER_ACCURACY 1 //定義時(shí)鐘分辨率,以ms為單位 UINT wTimerRes_1ms,wTimerRes_2s; //定義時(shí)間間隔 UINT wAccuracy; //定義分辨率 UINT TimerID_1ms,TimerID_2s; //定義定時(shí)器句柄///////////////////////////////CCureApp::CCureApp():fout("cure.out", ios::out) //打開輸出文件"cure.out";{  // 給時(shí)間間隔變量賦值  wTimerRes_1ms = ONE_MILLI_SECOND; wTimerRes_2s = TWO_SECOND;  TIMECAPS tc; //利用函數(shù)timeGetDevCaps取出系統(tǒng)分辨率的取值范圍,如果無錯(cuò)則繼續(xù); if(timeGetDevCaps(&tc,sizeof(TIMECAPS))==TIMERR_NOERROR)  { wAccuracy=min(max(tc.wPeriodMin, //分辨率的值不能超出系統(tǒng)的取值范圍    TIMER_ACCURACY),tc.wPeriodMax); //調(diào)用timeBeginPeriod函數(shù)設(shè)置定時(shí)器的分辨率   timeBeginPeriod(wAccuracy);   //設(shè)置定時(shí)器 InitializeTimer();  } } CCureApp:: ~CCureApp() {  fout <<"結(jié)束時(shí)鐘"<< endl; //結(jié)束時(shí)鐘  timeKillEvent(TimerID_1ms); // 刪除兩個(gè)定時(shí)器  timeKillEvent(TimerID_2s); // 刪除設(shè)置的分辨率  timeEndPeriod(wAccuracy); } void CCureApp::InitializeTimer() { StartOneMilliSecondTimer();  StartTwoSecondTimer(); } //1ms定時(shí)器的回調(diào)函數(shù),類似于中斷處理程序,一定要聲明為全局PASCAL函數(shù),//否則編譯會有問題 void PASCAL OneMilliSecondProc(UINT wTimerID, UINT msg,DWORD dwUser,DWORD dwl,DWORD dw2) { // 定義計(jì)數(shù)器  static int ms = 0;  CCureApp *app = (CCureApp *)dwUser;  // 取得系統(tǒng)時(shí)間,以ms為單位  DWORD osBinaryTime = GetTickCount();  //輸出計(jì)數(shù)器值和當(dāng)前系統(tǒng)時(shí)間 app->fout<<++ms<<":1ms:" } // 加裝1ms定時(shí)器 void CCureApp::StartOneMilliSecondTimer() {  if((TimerID_1ms = timeSetEvent(wTimerRes_1ms, wAccuracy,   (LPTIMECALBACK) OneMil liSecondProc, // 回調(diào)函數(shù);   (DWORD)this, // 用戶傳送到回調(diào)函數(shù)的數(shù)據(jù);  TIME_PERIODIC)) == 0)//周期調(diào)用定時(shí)處理函數(shù); { AfxMessageBox("不能進(jìn)行定時(shí)!", MB_OK | MB_ICONASTERISK);  }  else   fout << "16ms 計(jì) 時(shí):" << endl; //不等于0表明加裝成功,返回此定時(shí)器的句柄; }

     

    在精度要求較高的情況下,如要求定時(shí)誤差不大于1ms時(shí),還可以利用GetTickCount()函數(shù)返回自計(jì)算機(jī)啟動(dòng)后的時(shí)間,該函數(shù)的返回值是DWORD型,表示以ms為單位的計(jì)算機(jī)啟動(dòng)后經(jīng)歷的時(shí)間間隔。通過兩次調(diào)用GetTickCount()函數(shù),然后控制它們的差值來取得定時(shí)效果.下列的代碼可以實(shí)現(xiàn)50ms的精確定時(shí),其誤差是毫秒級的。

    // 起始值和中止值DWORD dwStart, dwStop ; dwStop = GetTickCount(); while(TRUE) {  // 上一次的中止值變成新的起始值  dwStart = dwStop ; // 此處添加相應(yīng)控制語句  do  {  dwStop = GetTickCount() ;  }while(dwStop - 50 < dwStart) ; }

     

    用上述兩種方式取得的定時(shí)效果雖然在許多場合已經(jīng)滿足實(shí)際的要求,但由于它們的精度只有毫秒級的,而且在要求定時(shí)時(shí)間間隔小時(shí),實(shí)際定時(shí)誤差大。對于精確度要求更高的定時(shí)操作,則應(yīng)該使用QueryPerformanceFrequency()和QueryPerformanceCounter()函數(shù)。這兩個(gè)函數(shù)是Visual C++提供并且僅供Windows 95及其后續(xù)版本使用,其精度與CPU的時(shí)鐘頻率有關(guān),它們要求計(jì)算機(jī)從硬件上支持精確定時(shí)器。QueryPerformanceFrequency()函數(shù)和QueryPerformanceCounter()函數(shù)的原型如下: 

    BOOL QueryPerformanceFrequency (LARGE_INTEGER *lpFrequency);BOOL QueryPerformanceCounter (LARGE_INTEGER *lpCount);

     

    上述兩個(gè)函數(shù)的參數(shù)的數(shù)據(jù)類型LARGE_INTEGER既可以是一個(gè)8字節(jié)長的整型數(shù),也可以是兩個(gè)4字節(jié)長的整型數(shù)的聯(lián)合結(jié)構(gòu),其具體用法根據(jù)編譯器是否支持64位而定。該類型的定義如下:

    typedef union _LARGE_INTEGER{ struct{   DWORD LowPart ; // 4字節(jié)整型數(shù)  LONG HighPart ; // 4字節(jié)整型數(shù) };  LONG QuadPart ; // 8字節(jié)整型數(shù)} LARGE_INTEGER ;

     

    使用QueryPerformanceFrequency()和QueryPerformanceCounter()函數(shù)進(jìn)行精確定時(shí)的步驟如下:

     

    1、首先調(diào)用QueryPerformanceFrequency()函數(shù)取得高精度運(yùn)行計(jì)數(shù)器的頻率f,單位是每秒多少次(n/s),此數(shù)一般很大;

     

    2、在需要定時(shí)的代碼的兩端分別調(diào)用QueryPerformanceCounter()以取得高精度運(yùn)行計(jì)數(shù)器的數(shù)值n1、n2,兩次數(shù)值的差值通過f換算成時(shí)間間隔,t=(n2-n1)/f,當(dāng)t大于或等于定時(shí)時(shí)間長度時(shí),啟動(dòng)定時(shí)器;

     

    二、編程步驟

     

    1、啟動(dòng)Visual C++6.0,生成一個(gè)基于對話框的應(yīng)用程序,將程序命名為"HightTimer";

     

    2、在對話框面板中添加控件,布局如圖一所示,其中包含兩個(gè)靜態(tài)文本框,兩個(gè)編輯框和兩個(gè)按紐。上面和下面位置的編輯框的ID分別為IDC_TEST和IDC_ACTUAL,"EXIT"按紐的ID為IDOK,"TEST"按紐ID為ID_TEST;

     

    3、通過Class Wizard添加成員變量,兩個(gè)編輯框控件分別對應(yīng)為DWORD m_dwTest和DWORD m_dwAct,另外添加"TEST"按紐的鼠標(biāo)單擊消息處理函數(shù);

     

    4、添加代碼,編譯運(yùn)行程序。

     

    三、程序代碼

    /////////////////////////////////////////////////////////////////////////LARGE_INTEGER MySleep(LARGE_INTEGER Interval) // 功能:執(zhí)行實(shí)際的延時(shí)功能,Interval 參數(shù)為需要執(zhí)行的延時(shí)與時(shí)間有關(guān)的數(shù)量,此函數(shù)返回執(zhí)//行后實(shí)際所用的時(shí)間有關(guān)的數(shù)量 ; {  LARGE_INTEGER privious, current, Elapse;  QueryPerformanceCounter( &privious );  current = privious;  while( current.QuadPart - privious.QuadPart < Interval.QuadPart ) QueryPerformanceCounter( ¤t );   Elapse.QuadPart = current.QuadPart - privious.QuadPart;  return Elapse; }void CHightTimerDlg::OnTest() { // TODO: Add your control notification handler code here UpdateData(TRUE); //取輸入的測試時(shí)間值到與編輯框相關(guān)聯(lián)的成員變量m_dwTest ; LARGE_INTEGER frequence; //取高精度運(yùn)行計(jì)數(shù)器的頻率,若硬件不支持則返回FALSE if(!QueryPerformanceFrequency( &frequence)) MessageBox("Your computer hardware doesn't support the high-resolution performance counter", "Not Support", MB_ICONEXCLAMATION | MB_OK); LARGE_INTEGER test, ret; //通過頻率換算微秒數(shù)到對應(yīng)的數(shù)量(與CPU時(shí)鐘有關(guān)),1=1000000微秒; test.QuadPart = frequence.QuadPart * m_dwTest / 1000000;  ret = MySleep( test ); //調(diào)用此函數(shù)開始延時(shí),返回實(shí)際花銷的數(shù)量 ; m_dwAct = (DWORD)(1000000 * ret.QuadPart / frequence.QuadPart ); //換算到微秒數(shù); UpdateData(FALSE); //顯示到對話框面板 ;}

     

    四、小結(jié)

     

    本實(shí)例介紹了實(shí)現(xiàn)精確定時(shí)的不同方法,尤其是對于需要精確到微秒級別的定時(shí)處理,給出了實(shí)現(xiàn)的方法和代碼,細(xì)心的讀者朋友在運(yùn)行程序的過程中可能會發(fā)現(xiàn)要求的定時(shí)長度和實(shí)際返回的時(shí)間長度還是有一些差異的,造成上述情況的原因是由于在進(jìn)行定時(shí)處理時(shí),還需要運(yùn)行一些簡單的循環(huán)代碼,所以會產(chǎn)生微秒級的誤差

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    Windows下的高精度計(jì)時(shí)和高頻事件的產(chǎn)生

     

    作者:戎亞新

    南京航空航天大學(xué)仿真與控制實(shí)驗(yàn)室

    下載源代碼

     

    在開發(fā) Windows 下的應(yīng)用程序時(shí),經(jīng)常需要用的計(jì)時(shí),尤其在一些對時(shí)間要求比較高的程序中,計(jì)時(shí)的精確性是很重要的,本文介紹了兩種精確計(jì)時(shí)的方法,計(jì)時(shí)的精度可以達(dá)到ms級,而且可以認(rèn)為它是精確的,可以在大多數(shù)情況下作為時(shí)間的基準(zhǔn)。

    1. 用API函數(shù)::timeGetTime()獲取從開機(jī)到現(xiàn)在經(jīng)過的ms數(shù),它的返回類型為DWORD類型,因此它的最大計(jì)時(shí)長度為2^32ms,約等于49天,::timeGetTime()是一個(gè)多媒體函數(shù),所以它的優(yōu)先級是很高的,一般可以將它看成是精確的。

    2. 用查詢系統(tǒng)定時(shí)器的計(jì)數(shù)值的方法,用到的API函數(shù)是QueryPerformanceCounter、QueryPerformanceFrequency,方法是用當(dāng)前計(jì)數(shù)值減去開始計(jì)時(shí)時(shí)刻的計(jì)數(shù)值,得到計(jì)數(shù)差值,再除以系統(tǒng)定時(shí)器的頻率就是計(jì)的時(shí)間,通常系統(tǒng)定時(shí)器的頻率非常高,我在 intel845e 的主板上達(dá)到了3579545hz,當(dāng)然對于不同的主板,它的頻率是不同的。程序運(yùn)行的結(jié)果 如圖一所示:

     

     

    圖一

    這種計(jì)時(shí)方法要用另外一個(gè)線程專門來查詢系統(tǒng)定時(shí)器的計(jì)數(shù)值,這就用到了多線程的知識。由于線程的調(diào)用是需要處理器時(shí)間的,所以在本中,多線程定時(shí)器的時(shí)間總要落后于多媒體定時(shí)器時(shí)間。但在中間的任何一個(gè)讀取時(shí)間的時(shí)刻都是非常精確的,只是從讀取到顯示有一個(gè)延遲過程。

    下面講一下Windows高頻事件的產(chǎn)生,還是利用上面兩種方法,Windows下有一個(gè)多媒體定時(shí)器,用法為一組API函數(shù)的調(diào)用,它們是:

    MMRESULT timeBeginPeriod(  UINT uPeriod ) ;

     

    MMRESULT timeSetEvent( UINT uDelay,

                           UINT uResolution,          

                           LPTIMECALLBACK lpTimeProc,  

                           DWORD dwUser,              

                           UINT fuEvent                

    );

     

    void CALLBACK TimeProc( UINT uID,      

                            UINT uMsg,    

                            DWORD dwUser,  

                            DWORD dw1,    

                            DWORD dw2      

    );

     

    MMRESULT timeKillEvent( UINT uTimerID );

    MMRESULT timeEndPeriod( UINT uPeriod );

    其中timeBeginPeriod是用來設(shè)置最高定時(shí)精度的,最高精度為1ms,如果要產(chǎn)生間隔為1ms的中斷,必須調(diào)用timeBeginPeriod(1);當(dāng)定時(shí)器用完之后就要用timeEndPeriod(1);來恢復(fù)默認(rèn)的精度。具體使用方法為在timeBeginPeriod(1)調(diào)用之后用timeSetEvent()注冊一個(gè)回調(diào)函數(shù),即一個(gè)中斷處理過程。它還可以向回調(diào)函數(shù)傳遞一個(gè)參數(shù),通??梢詡魉鸵粋€(gè)窗口句柄之類的東西。而回調(diào)函數(shù)TimeProc則從dwwUser參數(shù)中取出傳遞的參數(shù)使用。在Windows下,可以用這種方法進(jìn)行1ms精度的定時(shí)數(shù)據(jù)采集,數(shù)據(jù)發(fā)送,但要保證1ms能完成所有的操作和運(yùn)算。本人經(jīng)過實(shí)踐證明,用它來實(shí)現(xiàn)控制的精度是足夠的。

    第二種方法還是使用多線程查詢系統(tǒng)定時(shí)器計(jì)數(shù)值,它與上面提到的方法相比有優(yōu)點(diǎn)也有缺點(diǎn),缺點(diǎn)是精度不夠高,優(yōu)點(diǎn)是產(chǎn)生的間隔能突破1ms的限制,可以達(dá)到更小的間隔,理論上事件產(chǎn)生的頻率可以和系統(tǒng)定時(shí)器的頻率一樣。主要示例代碼如下:

    UINT Timer(LPVOID pParam)

    {

    QueryPerformanceCounter((LARGE_INTEGER *)& gl_BeginTime );

    while(gl_bStart)

    {

    QueryPerformanceCounter((LARGE_INTEGER *)&gl_CurrentTime );

    If(gl_CurrentTime - gl_BeginTime > 1.0/Interval )

    {

    //定時(shí)的事件,比如發(fā)送數(shù)據(jù)到端口,采集數(shù)據(jù)等

    gl_BeginTime = gl_CurrentTime;

    }

    }

    return 1;

    }      

    這是多線程中的一個(gè)線程函數(shù),Interval是產(chǎn)生事件的間隔,如果為0.001則為1ms產(chǎn)生一次,理論上如果Interval為1,則以最大的頻率產(chǎn)生事件。即可以用Windows產(chǎn)生很高頻率的事件,但是由于線程的調(diào)用是要有時(shí)間的,有的時(shí)候可能會造成這個(gè)線程一直沒有得到執(zhí)行,從而造成有一段時(shí)間沒有進(jìn)行計(jì)數(shù),這段時(shí)間的定時(shí)事件就沒有產(chǎn)生了,如果定時(shí)的頻率越高,丟失的可能性就越大。但如果用它來產(chǎn)生高頻隨時(shí)間變化的隨機(jī)信號還是很有價(jià)值的。這在實(shí)時(shí)仿真中尤其如此。

     

    具體的實(shí)現(xiàn)請參看詳細(xì)的例子代碼。

     

    如有疑問,請與我聯(lián)系:

    qq:21881480

    email:bsrong@elong.com 或 bsrong_nuaa@msn.com

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    1.project   setting   link設(shè)置winmm.lib     

      2.#include   <afxmt.h>  

          #include   "mmsystem.h"  

      3.  

          DWORD   dwOldTime=timeGetTime();  

          //...  

          DWORD   dwCurTime=timeGetTime();  

          DWORD   dwLen   =   dwCurTime   -   dwOldTime;  

     

     

     

    使用多媒體定時(shí)器還需要加入   winmm.lib   庫文件!  (project-setting里面)

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    主題 VC++的鏈接錯(cuò)誤LNK2001(ZT)

    學(xué)習(xí)VC++時(shí)經(jīng)常會遇到鏈接錯(cuò)誤LNK2001,該錯(cuò)誤非常討厭,因?yàn)閷τ?/p>

    編程者來說,最好改的錯(cuò)誤莫過于編譯錯(cuò)誤,而一般說來發(fā)生連接錯(cuò)誤時(shí),

    編譯都已通過。產(chǎn)生連接錯(cuò)誤的原因非常多,尤其LNK2001錯(cuò)誤,常常使人不

    明其所以然。如果不深入地學(xué)習(xí)和理解VC++,要想改正連接錯(cuò)誤LNK2001非

    常困難。

    初學(xué)者在學(xué)習(xí)VC++的過程中,遇到的LNK2001錯(cuò)誤的錯(cuò)誤消息主要為:

    unresolved external symbol “symbol”(不確定的外部“符號”)。

    如果連接程序不能在所有的庫和目標(biāo)文件內(nèi)找到所引用的函數(shù)、變量或

    標(biāo)簽,將產(chǎn)生此錯(cuò)誤消息。一般來說,發(fā)生錯(cuò)誤的原因有兩個(gè):一是所引用

    的函數(shù)、變量不存在、拼寫不正確或者使用錯(cuò)誤;其次可能使用了不同版本

    的連接庫。

    以下是可能產(chǎn)生LNK2001錯(cuò)誤的原因:

    一.由于編碼錯(cuò)誤導(dǎo)致的LNK2001。

    1.不相匹配的程序代碼或模塊定義(.DEF)文件能導(dǎo)致LNK2001。例如,

    如果在C++ 源文件內(nèi)聲明了一變量“var1”,卻試圖在另一文件內(nèi)以變量

    “VAR1”訪問該變量,將發(fā)生該錯(cuò)誤。

    2.如果使用的內(nèi)聯(lián)函數(shù)是在.CPP文件內(nèi)定義的,而不是在頭文件內(nèi)定

    義將導(dǎo)致LNK2001錯(cuò)誤。

    3.調(diào)用函數(shù)時(shí)如果所用的參數(shù)類型同函數(shù)聲明時(shí)的類型不符將會產(chǎn)生

    LNK2001。

    4.試圖從基類的構(gòu)造函數(shù)或析構(gòu)函數(shù)中調(diào)用虛擬函數(shù)時(shí)將會導(dǎo)致LNK2001。

    5.要注意函數(shù)和變量的可公用性,只有全局變量、函數(shù)是可公用的。

    靜態(tài)函數(shù)和靜態(tài)變量具有相同的使用范圍限制。當(dāng)試圖從文件外部訪問

    任何沒有在該文件內(nèi)聲明的靜態(tài)變量時(shí)將導(dǎo)致編譯錯(cuò)誤或LNK2001。

    函數(shù)內(nèi)聲明的變量(局部變量) 只能在該函數(shù)的范圍內(nèi)使用。

    C++ 的全局常量只有靜態(tài)連接性能。這不同于C,如果試圖在C++的

    多個(gè)文件內(nèi)使用全局變量也會產(chǎn)生LNK2001錯(cuò)誤。一種解決的方法是需要時(shí)在

    頭文件中加入該常量的初始化代碼,并在.CPP文件中包含該頭文件;另一種

    方法是使用時(shí)給該變量賦以常數(shù)。

    二.由于編譯和鏈接的設(shè)置而造成的LNK2001

    1.如果編譯時(shí)使用的是/NOD(/NODEFAULTLIB)選項(xiàng),程序所需要的運(yùn)行

    庫和MFC庫在連接時(shí)由編譯器寫入目標(biāo)文件模塊, 但除非在文件中明確包含

    這些庫名,否則這些庫不會被鏈接進(jìn)工程文件。在這種情況下使用/NOD將導(dǎo)

    致錯(cuò)誤LNK2001。

    2.如果沒有為wWinMainCRTStartup設(shè)定程序入口,在使用Unicode和MFC

    時(shí)將得到“unresolved external on _WinMain@16”的LNK2001錯(cuò)誤信息。

    3.使用/MD選項(xiàng)編譯時(shí),既然所有的運(yùn)行庫都被保留在動(dòng)態(tài)鏈接庫之內(nèi),

    源文件中對“func”的引用,在目標(biāo)文件里即對“__imp__func” 的引用。

    如果試圖使用靜態(tài)庫LIBC.LIB或LIBCMT.LIB進(jìn)行連接,將在__imp__func上發(fā)

    生LNK2001;如果不使用/MD選項(xiàng)編譯,在使用MSVCxx.LIB連接時(shí)也會發(fā)生LNK2001。

    4.使用/ML選項(xiàng)編譯時(shí),如用LIBCMT.LIB鏈接會在_errno上發(fā)生LNK2001。

    5.當(dāng)編譯調(diào)試版的應(yīng)用程序時(shí),如果采用發(fā)行版模態(tài)庫進(jìn)行連接也會產(chǎn)

    生LNK2001;同樣,使用調(diào)試版模態(tài)庫連接發(fā)行版應(yīng)用程序時(shí)也會產(chǎn)生相同的

    問題。

    6.不同版本的庫和編譯器的混合使用也能產(chǎn)生問題,因?yàn)樾掳娴膸炖锟?/p>

    能包含早先的版本沒有的符號和說明。

    7.在不同的模塊使用內(nèi)聯(lián)和非內(nèi)聯(lián)的編譯選項(xiàng)能夠?qū)е翷NK2001。如果

    創(chuàng)建C++庫時(shí)打開了函數(shù)內(nèi)聯(lián)(/Ob1或/Ob2),但是在描述該函數(shù)的相應(yīng)頭

    文件里卻關(guān)閉了函數(shù)內(nèi)聯(lián)(沒有inline關(guān)鍵字),這時(shí)將得到該錯(cuò)誤信息。

    為避免該問題的發(fā)生,應(yīng)該在相應(yīng)的頭文件中用inline關(guān)鍵字標(biāo)志內(nèi)聯(lián)函數(shù)。

    8.不正確的/SUBSYSTEM或/ENTRY設(shè)置也能導(dǎo)致LNK2001。

    其實(shí),產(chǎn)生LNK2001的原因還有很多,以上的原因只是一部分而已,對初

    學(xué)者來說這些就夠理解一陣子了。但是,分析錯(cuò)誤原因的目的是為了避免錯(cuò)

    誤的發(fā)生。LNK2001錯(cuò)誤雖然比較困難,但是只要注意到了上述問題,還是能

    夠避免和予以解決的。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    1. Windows子系統(tǒng)設(shè)置錯(cuò)誤, 提示:

    libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main

    Windows項(xiàng)目要使用Windows子系統(tǒng), 而不是Console, 可以這樣設(shè)置:

    [Project] --> [Settings] --> 選擇"Link"屬性頁,

    在Project Options中將/subsystem:console改成/subsystem:windows

     

    2. Console子系統(tǒng)設(shè)置錯(cuò)誤, 提示:

    LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16

    控制臺項(xiàng)目要使用Console子系統(tǒng), 而不是Windows, 設(shè)置:

    [Project] --> [Settings] --> 選擇"Link"屬性頁,

    在Project Options中將/subsystem:windows改成/subsystem:console

     

    3. 程序入口設(shè)置錯(cuò)誤, 提示:

    msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16

    通常, MFC項(xiàng)目的程序入口函數(shù)是WinMain, 如果編譯項(xiàng)目的Unicode版本, 程序入口必須改為wWinMainCRTStartup, 所以需要重新設(shè)置程序入口:

    [Project] --> [Settings] --> 選擇"Link"屬性頁,

    在Category中選擇Output,

    再在Entry-point symbol中填入wWinMainCRTStartup, 即可

     

    (發(fā)表于2006-10-8 16:49:00)

     

    adam830:4. 線程運(yùn)行時(shí)庫設(shè)置錯(cuò)誤, 提示:

    nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex

    nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex

    這是因?yàn)镸FC要使用多線程時(shí)庫, 需要更改設(shè)置:

    [Project] --> [Settings] --> 選擇"C/C++"屬性頁,

    在Category中選擇Code Generation,

    再在Use run-time library中選擇Debug Multithreaded或者multithreaded

    其中,

    Single-Threaded 單線程靜態(tài)鏈接庫(release版本)

    Multithreaded 多線程靜態(tài)鏈接庫(release版本)

    multithreaded DLL 多線程動(dòng)態(tài)鏈接庫(release版本)

    Debug Single-Threaded 單線程靜態(tài)鏈接庫(debug版本)

    Debug Multithreaded 多線程靜態(tài)鏈接庫(debug版本)

    Debug Multithreaded DLL 多線程動(dòng)態(tài)鏈接庫(debug版本)

    單線程: 不需要多線程調(diào)用時(shí), 多用在DOS環(huán)境下

    多線程: 可以并發(fā)運(yùn)行

    靜態(tài)庫: 直接將庫與程序Link, 可以脫離MFC庫運(yùn)行

    動(dòng)態(tài)庫: 需要相應(yīng)的DLL動(dòng)態(tài)庫, 程序才能運(yùn)行

    release版本: 正式發(fā)布時(shí)使用

    debug版本: 調(diào)試階段使用

    (發(fā)表于2006-10-8 16:49:00)

     

     

    多媒體定時(shí)器對實(shí)現(xiàn)高精度定時(shí)是很理想的工具,而且其精度是十分可靠的。但是,多媒體定時(shí)器也并不是完美的。因?yàn)樗煽康木仁墙⒃趯ο到y(tǒng)資源的消耗之上的。因此,在利用多媒體定時(shí)器完成工作的同時(shí),必須注意以下幾點(diǎn):

    ---- 1. 多媒體定時(shí)器的設(shè)置分辨率不能超出系統(tǒng)許可范圍。

     

    ---- 2. 在使用完定時(shí)器以后,一定要及時(shí)刪除定時(shí)器及其分辨率,否則系統(tǒng)會越來越慢。

     

    ---- 3. 多媒體定時(shí)器在啟動(dòng)時(shí),將自動(dòng)開辟一個(gè)獨(dú)立的線程。在定時(shí)器線程結(jié)束之前,注意一定不能再次啟動(dòng)該定時(shí)器,不然將迅速造成死機(jī)。

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多