2010-09-19 76 views
52

ध्यान दें कि मैं ऐसा कुछ पूछ रहा हूं जो System.Threading.Timer जैसे कुछ का उपयोग कर कॉलबैक फ़ंक्शन को हर 15 एमएस से अधिक बार कॉल करेगा। मैं System.Diagnostics.Stopwatch या यहां तक ​​कि QueryPerformanceCounter जैसे कुछ का उपयोग करके कोड के टुकड़े को सटीक रूप से सही करने के बारे में नहीं पूछ रहा हूं।क्यों .NET टाइमर 15 एमएस रिज़ॉल्यूशन तक सीमित हैं?

इसके अलावा, मैं संबंधित प्रश्नों पढ़ा है:

Accurate Windows timer? System.Timers.Timer() is limited to 15 msec

High resolution timer in .NET

न तो जिनमें से मेरे सवाल के लिए एक उपयोगी जवाब आपूर्ति करती है।

इसके अलावा, की सिफारिश की MSDN लेख, Implement a Continuously Updating, High-Resolution Time Provider for Windows, चीजें समय के बजाय टिक की एक सतत स्ट्रीम प्रदान करने के बारे में है।

इसके साथ कहा। । ।

वहाँ नेट टाइमर वस्तुओं के बारे में बुरा जानकारी की एक पूरी बहुत सी चीज़ें हैं। उदाहरण के लिए, System.Timers.Timer "सर्वर अनुप्रयोगों के लिए अनुकूलित एक उच्च प्रदर्शन टाइमर" के रूप में बिल किया जाता है। और System.Threading.Timer किसी भी तरह से द्वितीय श्रेणी के नागरिक माना जाता है। पारंपरिक ज्ञान यह है कि System.Threading.Timer विंडोज Timer Queue Timers के आसपास एक रैपर है और System.Timers.Timer पूरी तरह से कुछ और है।

वास्तविकता बहुत अलग है। System.Timers.TimerSystem.Threading.Timer के आस-पास केवल एक पतला घटक रैपर है (केवल System.Timers.Timer के अंदर देखने के लिए परावर्तक या आईएलडीएएसएम का उपयोग करें और आपको System.Threading.Timer का संदर्भ दिखाई देगा), और इसमें कुछ कोड है जो स्वचालित थ्रेड सिंक्रनाइज़ेशन प्रदान करेगा ताकि आपको इसे करने की आवश्यकता न हो।

System.Threading.Timer, क्योंकि यह टाइमर कतार टाइमर के लिए एक रैपर नहीं है। कम से कम 2.0 रनटाइम में नहीं, जिसका उपयोग .NET 2.0 से .NET 3.5 के माध्यम से किया गया था। साझा स्रोत सीएलआई के साथ कुछ मिनट दिखाते हैं कि रनटाइम टाइमर कतार टाइमर के समान ही अपनी टाइमर कतार लागू करता है, लेकिन वास्तव में Win32 फ़ंक्शंस को कभी भी कॉल नहीं करता है।

ऐसा लगता है कि .NET 4.0 क्रम भी अपने स्वयं के टाइमर कतार लागू करता है। मेरा परीक्षण कार्यक्रम (नीचे देखें) .NET 4.0 के तहत समान परिणाम प्रदान करता है क्योंकि यह .NET 3.5 के अंतर्गत होता है। मैंने टाइमर क्यूई टाइमर के लिए अपना स्वयं का प्रबंधित रैपर बनाया है और साबित किया है कि मुझे 1 एमएस रिज़ॉल्यूशन (काफी अच्छी सटीकता के साथ) मिल सकता है, इसलिए मुझे लगता है कि मैं सीएलआई स्रोत गलत पढ़ रहा हूं।

सबसे पहले, क्या इतनी धीमी गति से होने के लिए टाइमर कतार के क्रम के कार्यान्वयन का कारण बनता है:

मैं दो प्रश्न हैं? मैं 15 एमएस रिज़ॉल्यूशन से बेहतर नहीं हो सकता, और सटीकता -1 से +30 एमएस की सीमा में प्रतीत होती है। यही है, अगर मैं 24 एमएस पूछता हूं, तो मुझे 23 से 54 एमएस के अलावा कहीं भी टिक मिलेंगे। मुझे लगता है कि मैं जवाब को ट्रैक करने के लिए सीएलआई स्रोत के साथ कुछ और समय बिता सकता हूं, लेकिन सोचा कि यहां किसी को पता चल सकता है।

दूसरा, और मुझे लगता है कि यह जवाब देने के लिए कठिन है, यही कारण है कि टाइमर कतार टाइमर का उपयोग नहीं? मुझे एहसास है कि .NET 1.x को Win9x पर चलाना था, जिसमें उन एपीआई नहीं थे, लेकिन वे विंडोज 2000 के बाद से मौजूद हैं, जो मुझे याद है कि .NET 2.0 के लिए न्यूनतम आवश्यकता सही थी। क्या ऐसा इसलिए है क्योंकि सीएलआई को गैर-विंडोज़ बॉक्स पर चलाना पड़ा था?

मेरे टाइमर परीक्षण कार्यक्रम:

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Threading; 

namespace TimerTest 
{ 
    class Program 
    { 
     const int TickFrequency = 5; 
     const int TestDuration = 15000; // 15 seconds 

     static void Main(string[] args) 
     { 
      // Create a list to hold the tick times 
      // The list is pre-allocated to prevent list resizing 
      // from slowing down the test. 
      List<double> tickTimes = new List<double>(2 * TestDuration/TickFrequency); 

      // Start a stopwatch so we can keep track of how long this takes. 
      Stopwatch Elapsed = Stopwatch.StartNew(); 

      // Create a timer that saves the elapsed time at each tick 
      Timer ticker = new Timer((s) => 
       { 
        tickTimes.Add(Elapsed.ElapsedMilliseconds); 
       }, null, 0, TickFrequency); 

      // Wait for the test to complete 
      Thread.Sleep(TestDuration); 

      // Destroy the timer and stop the stopwatch 
      ticker.Dispose(); 
      Elapsed.Stop(); 

      // Now let's analyze the results 
      Console.WriteLine("{0:N0} ticks in {1:N0} milliseconds", tickTimes.Count, Elapsed.ElapsedMilliseconds); 
      Console.WriteLine("Average tick frequency = {0:N2} ms", (double)Elapsed.ElapsedMilliseconds/tickTimes.Count); 

      // Compute min and max deviation from requested frequency 
      double minDiff = double.MaxValue; 
      double maxDiff = double.MinValue; 
      for (int i = 1; i < tickTimes.Count; ++i) 
      { 
       double diff = (tickTimes[i] - tickTimes[i - 1]) - TickFrequency; 
       minDiff = Math.Min(diff, minDiff); 
       maxDiff = Math.Max(diff, maxDiff); 
      } 

      Console.WriteLine("min diff = {0:N4} ms", minDiff); 
      Console.WriteLine("max diff = {0:N4} ms", maxDiff); 

      Console.WriteLine("Test complete. Press Enter."); 
      Console.ReadLine(); 
     } 
    } 
} 
+0

अच्छा सवाल! मुझे दिलचस्पी है अगर किसी को वास्तव में इसमें अंतर्दृष्टि है। एसओ पर क्लियर टीम से कोई भी? –

+0

मैं शर्त लगाता हूं कि Win9x में भी कर्नेल में टिकटें थीं। यह सब के बाद एक preemptive कार्य वातावरण था। सभी shitty लेकिन अभी भी असली multitask। इसलिए आप हस्तक्षेप के बिना छूट नहीं कर सकते हैं, और वे एचटीसी (64 हर्ट्ज) के आधार पर नियमित हार्डवेयर टाइमर से विशेष रूप से 90 के दशक में आग लगते हैं। आप ईवेंट कतार के बिना जीयूआई कैसे करते हैं? टाइमर घटनाओं को कर्नेल द्वारा इवेंट कतार में धकेल दिया जाता है, इसके चारों ओर कोई रास्ता नहीं है क्योंकि आपका ऐप कतार के लिए कुछ भी नहीं कर रहा है, यह निर्धारित है। –

उत्तर

27

शायद दस्तावेज़ यहाँ जुड़े हुए यह थोड़ा बताते हैं।

प्रणाली टाइमर संकल्प निर्धारित करता है कि कितने समय में विंडोज दो मुख्य कार्य करता है:

  • अद्यतन टाइमर टिक यह थोड़े सूखा तो मैं केवल ब्राउज़ इसे जल्दी :)

    परिचय का हवाला देते हुए है गिनती है कि एक पूर्ण टिक समाप्त हो गया है।

  • जांचें कि निर्धारित शेड्यूल ऑब्जेक्ट समाप्त हो गया है या नहीं।

एक टाइमर टिक बीत समय है कि विंडोज दिन के समय पर नज़र रखने और क्वांटम बार सूत्र में बाँधना का उपयोग करता है की एक धारणा है। डिफ़ॉल्ट रूप से, घड़ी बाधित होती है और टाइमर टिक समान होती है, लेकिन विंडोज या कोई एप्लिकेशन इंटरप्ट अवधि को बदल सकता है।

डिफ़ॉल्ट टाइमर विंडोज 7 पर संकल्प 15.6 मिलीसेकंड (एमएस) है। कुछ अनुप्रयोग इसे 1 एमएस तक कम करते हैं, जो मोबाइल सिस्टम पर बैटरी रन टाइम तक 25 प्रतिशत तक कम कर देता है।

मूल रूप से: Timers, Timer Resolution, and Development of Efficient Code (docx).

+0

लिंक के लिए धन्यवाद, अर्नोल्ड। यह मेरे प्रश्न का पूरी तरह उत्तर नहीं देता है, लेकिन यह कुछ अंतर्दृष्टि देता है, खासकर 15.6 एमएस डिफ़ॉल्ट टाइमर रिज़ॉल्यूशन के बारे में थोड़ा सा। –

+0

हां, मुझे यह महसूस नहीं हुआ कि यह सब कुछ समझाया गया लेकिन कुछ रोचक जानकारी थी। –

+0

थ्रेड में जवाब भी देखें ["सी # से 1 एमएस तक टाइमर रिज़ॉल्यूशन कैसे सेट करें?"] (Http://stackoverflow.com/questions/15071359)। –

13

टाइमर संकल्प प्रणाली दिल की धड़कन द्वारा दिया जाता है। यह आम तौर पर 64 बीट्स/एस के लिए डिफ़ॉल्ट है जो 15.625 एमएस है। लेकिन वहाँ 1 एमएस या नए प्लेटफॉर्म पर भी 0.5 एमएस करने के लिए नीचे टाइमर संकल्प को प्राप्त करने के लिए इन प्रणाली विस्तृत सेटिंग्स को संशोधित करने तरीके हैं:

1. मल्टीमीडिया टाइमर इंटरफेस के माध्यम से 1 एमएस समाधान के लिए जा रहे हैं:

मल्टीमीडिया टाइमर इंटरफ़ेस 1 एमएस रिज़ॉल्यूशन प्रदान करने में सक्षम है। timeBeginPeriod के बारे में अधिक जानकारी के लिए About Multimedia Timers (एमएसडीएन), Obtaining and Setting Timer Resolution (एमएसडीएन), और this उत्तर देखें। नोट: पूर्ण होने पर डिफ़ॉल्ट टाइमर रिज़ॉल्यूशन पर वापस जाने के लिए timeEndPeriod पर कॉल करना न भूलें।

कैसे करना है:

#define TARGET_RESOLUTION 1   // 1-millisecond target resolution 

TIMECAPS tc; 
UINT  wTimerRes; 

if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) 
{ 
    // Error; application can't continue. 
} 

wTimerRes = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax); 
timeBeginPeriod(wTimerRes); 

//  do your stuff here at approx. 1 ms timer resolution 

timeEndPeriod(wTimerRes); 

नोट: यह प्रक्रिया के रूप में अच्छी तरह से अन्य प्रक्रियाओं के लिए उपलब्ध है और प्राप्त संकल्प विस्तृत प्रणाली लागू होता है। किसी भी प्रक्रिया द्वारा अनुरोध किया गया उच्चतम संकल्प सक्रिय होगा, परिणामों को ध्यान में रखेगा।

2. 0,5 एमएस समाधान करने के लिए जा रहे हैं:

आप 0.5 एमएस छिपा एपीआई NtSetTimerResolution() के माध्यम से संकल्प प्राप्त कर सकते हैं। NtSetTimerResolution मूल विंडोज एनटी पुस्तकालय NTDLL.DLL द्वारा निर्यात किया जाता है। एमएसडीएन पर How to set timer resolution to 0.5ms ? देखें। फिर भी, वास्तविक प्राप्त करने योग्य संकल्प अंतर्निहित हार्डवेयर द्वारा निर्धारित किया जाता है। आधुनिक हार्डवेयर 0.5 एमएस संकल्प का समर्थन करता है। Inside Windows NT High Resolution Timers में और भी अधिक जानकारी मिलती है। समर्थित संकल्प NtQueryTimerResolution() को कॉल द्वारा प्राप्त किए जा सकते हैं।

कैसे करना है:

#define STATUS_SUCCESS 0 
#define STATUS_TIMER_RESOLUTION_NOT_SET 0xC0000245 

// after loading NtSetTimerResolution from ntdll.dll: 

// The requested resolution in 100 ns units: 
ULONG DesiredResolution = 5000; 
// Note: The supported resolutions can be obtained by a call to NtQueryTimerResolution() 

ULONG CurrentResolution = 0; 

// 1. Requesting a higher resolution 
// Note: This call is similar to timeBeginPeriod. 
// However, it to to specify the resolution in 100 ns units. 
if (NtSetTimerResolution(DesiredResolution ,TRUE,&CurrentResolution) != STATUS_SUCCESS) { 
    // The call has failed 
} 

printf("CurrentResolution [100 ns units]: %d\n",CurrentResolution); 
// this will show 5000 on more modern platforms (0.5ms!) 

//  do your stuff here at 0.5 ms timer resolution 

// 2. Releasing the requested resolution 
// Note: This call is similar to timeEndPeriod 
switch (NtSetTimerResolution(DesiredResolution ,FALSE,&CurrentResolution) { 
    case STATUS_SUCCESS: 
     printf("The current resolution has returned to %d [100 ns units]\n",CurrentResolution); 
     break; 
    case STATUS_TIMER_RESOLUTION_NOT_SET: 
     printf("The requested resolution was not set\n"); 
     // the resolution can only return to a previous value by means of FALSE 
     // when the current resolution was set by this application  
     break; 
    default: 
     // The call has failed 

} 

नोट: NtSetTImerResolution की कार्यक्षमता मूल रूप से bool मूल्य Set का उपयोग करके कार्यों timeBeginPeriodऔरtimeEndPeriod को मैप किया है (और योजना के बारे में अधिक जानकारी के लिए Inside Windows NT High Resolution Timers देखना इसके सभी प्रभाव)। हालांकि, मल्टीमीडिया सूट ग्रैन्युलरिटी को मिलीसेकंड तक सीमित करता है और एनटीएसएटीटीमर रेसोल्यूशन उप-मिलीसेकंड मान सेट करने की अनुमति देता है।

+0

मेरे प्रयोगों के आधार पर मुझे लगता है कि STATUS_TIMER_RESOLUTION_NOT_SET त्रुटि कोड 0xC0000245 होना चाहिए 245 –

+0

@ रोलैंड पिहलाकास यह सही है, धन्यवाद। मैंने इसे सही करने के लिए अपना जवाब संपादित कर लिया है। – Arno

संबंधित मुद्दे