只有QueryPerformanceCounter能突破1ms,內部就是一條彙編語句直接讀cpu晶振讀數,
但容易受到線程排隊和消息隊列延遲帶來的影響,不穩定
QueryPerformanceCounter
缺點是精度不夠高,優點是產生的間隔能突破1ms的限制,可以達到更小的間隔,理論上事件產生的頻率可以和系統定時器的頻率一樣
如果爲0.001則爲1ms產生一次,理論上如果Interval爲1,則以最大的頻率產生事件。即可以用Windows產生很高頻率的事件,但是由於線程的調用是要有時間的,有的時候可能會造成這個線程一直沒有得到執行,從而造成有一段時間沒有進行計數,這段時間的定時事件就沒有產生了,如果定時的頻率越高,丟失的可能性就越大。但如果用它來產生高頻隨時間變化的隨機信號還是很有價值的。這在實時仿真中尤其如此。
此外cpu佔用也很高,及時相應cpu就一定很忙
private void CountTime(long dwUs)
{
if (dwUs < 0) return;
long ctr1 = 0, ctr2 = 0;
if (freq == 0) QueryPerformanceFrequency(ref freq);
if (QueryPerformanceCounter(ref ctr1) != 0) // Begin timing.
{
do
{
QueryPerformanceCounter(ref ctr2); // Finish timing.
} while (((ctr2 - ctr1) * 1.0 * 1000000 / freq) < dwUs);
}
else
{
Thread.Sleep(Convert.ToInt32(dwUs / 1000));
}
}
[DllImport("kernel32.dll")]
extern static short QueryPerformanceFrequency(ref long x);
[DllImport("kernel32.dll")]
extern static short QueryPerformanceCounter(ref long x);
Windows下要實現穩定的1ms定時是不可能的,Windows本來就不是實時操作系統,當初的設計就是不用來高精度定時,CreateWaitableTimer,SetWaitableTimer 可以精確到100納秒,但是波動性仍然很大.另外多媒體定時器也可以實現1ms的定時,不過最多隻能開16個定時器,而且實際時間波動也不小
我們知道, 在linux上, sleep函數的單位是s, 那怎麼進行微妙級別的定時呢? 用select函數即可。 但是, 在Windows上, 強烈不建議將select函數用作定時器(該語句出自大名鼎鼎的Windows Socket這本書), 下面我們來實戰一下:
看程序:
- #include <winsock2.h>
- #include <stdio.h>
- #pragma comment(lib, "ws2_32.lib")
- int main()
- {
- WORD wVersionRequested;
- WSADATA wsaData;
- wVersionRequested = MAKEWORD(1, 1);
- WSAStartup( wVersionRequested, &wsaData );
- SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
- fd_set read_set;
- struct timeval t;
- FD_ZERO(&read_set);
- FD_SET(sockClient, &read_set);
- t.tv_sec = 3;
- t.tv_usec = 0;
- int ret = select(-1, NULL, NULL, NULL, &t);
- printf("ret is %d, %d, %d\n", ret, GetLastError(), WSAEINVAL);
- closesocket(sockClient);
- WSACleanup();
- return 0;
- }
爲什麼會這樣呢? Windows Sockets 專家Bob Quinn說過, 在Windows中, 如果select函數的第2, 3, 4個參數爲NULL, 那麼, select函數會經常返回失敗的-1.
好, 我們來改一下程序:
- #include <winsock2.h>
- #include <stdio.h>
- #pragma comment(lib, "ws2_32.lib")
- int main()
- {
- WORD wVersionRequested;
- WSADATA wsaData;
- wVersionRequested = MAKEWORD(1, 1);
- WSAStartup( wVersionRequested, &wsaData );
- SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
- fd_set read_set;
- struct timeval t;
- FD_ZERO(&read_set);
- FD_SET(sockClient, &read_set);
- t.tv_sec = 3;
- t.tv_usec = 0;
- int ret = select(-1, &read_set, NULL, NULL, &t); // 改動了
- printf("ret is %d, %d, %d\n", ret, GetLastError(), WSAEINVAL);
- closesocket(sockClient);
- WSACleanup();
- return 0;
- }
而且, 我們確實看到, 起到了3秒定時的作用。 但是, 我們看看, 這個定時明顯受制於sockClient啊, 根據之前博文的分析, 如果sockClient上有數據可讀, 那麼程序會立即返回1, 從而失去了定時的作用。
綜上所述, 在Windows上, 不要用select函數做定時器。 而在linux上, 這麼用很常見。