2017-07-06 3 views
-1

Ich versuche, eine Methode mit einer stabilen Zykluszeit (z. B. 20ms) auszuführen. Mein aktueller Ansatz besteht darin, einen Thread mit std::thread zu erstellen. Innerhalb dieses Threads ich folgendes (Pseudo-Code) zu tun:Stabile Zykluszeit

while(true) 
{ 
    tStart = GetCurrentTime(); 
    ExecuteMethod(); 
    tEnd = GetCurrentTime(); 

    actualCycleTime = tEnd - tStart; 
    SleepFor(DesiredCycleTime - actualCycleTime); 
} 

Für die Zeitmessung und Schlafen ich std::chrono (std::steady_clock und std::thread::sleep_for).

Das Problem ist, dass meine Schleife nicht bei den erwarteten stabilen 20 ms ausgeführt wird. Stattdessen erhalte ich Zykluszeiten zwischen 20 und 60ms. Meine Vermutung ist, dass dies durch den Windows-Scheduler verursacht wird.

Gibt es einen besseren Weg, um eine stabile Zykluszeit zu erreichen (busy waiting, etc.)?

+0

Sie verwenden ein wiederholtes relatives Timing, das anfällig für Rennbedingungen zwischen der Bestimmung der tatsächlichen Zykluszeit und der Festlegung der gewünschten Zykluszeit ist. Sie benötigen einen extern gesteuerten periodischen Trigger der gewünschten Zykluszeit. – Yunnosch

+0

Vielleicht möchten Sie sich https://stackoverflow.com/questions/2904887/sub-millisecond-precision-timing-in-c-or-c/2905082#2905082 ansehen. – jodag

+0

timeSetEvent war relativ zuverlässig, wenn es mit timeBeginPeriod verwendet wurde. MSDN sagt, dass es veraltet ist, und Sie sollten https://msdn.microsoft.com/en-us/library/ms682485(v=vs.85).aspx verwenden Ich habe keine Erfahrung damit, also kann ich nicht sagen wie zuverlässig oder genau ist es. –

Antwort

2

Sie könnten ein Timer-Ereignis verwenden. Wenn Sie eine wirklich solide Uhr benötigen, müssen Sie Ihre Prioritätsstufe auf das Maximum erhöhen. Dieser Code bietet Ihnen die bestmögliche Leistung für eine Anwendung im Benutzermodus. Ich habe die übliche Fehlerüberprüfung aus Gründen der Klarheit weggelassen, aber ich habe die Anrufe markiert, die überprüft werden sollten. Wenden Sie sich im Zweifelsfall an MSDN.

Windows-Zeitgeberauflösung ist auf eine globale Zeitscheibe beschränkt, die Windows zum Wechseln zwischen Threads verwendet. Bei modernen CPUs ist dieser Wert normalerweise 2 bis 5 ms. Bei älteren CPUs beträgt dieser Wert 10-15ms. Sie können diese globale Einstellung steuern, indem Sie timeBeginPeriod() aufrufen. Dies beeinflusst die Genauigkeit der Interrupts.

// use this event to exit the loop, by calling SetEvent(hExitEvent). 
HANDLE hExitEvent = CreateEvent(NULL, NULL, FALSE, NULL); 

void RealTimeLoop() 
{ 
    // You may want to raise the process priority... 
    HANDLE hProcess = GetCurrentProcess();      // never fails 
    SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS); 

    // setting the priority is critical. 
    HANDLE hThread = GetCurrentThread();       // never fails 
    SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL); // could fail 

    timeBeginPeriod(1);           // could fail 

    HANDLE hTimer = CreateWaitableTimer(NULL, FALSE, NULL);  // could fail 

    // could also set a call back here, but I've never tried it. 
    LARGE_INTEGER dueTime = {}; 
    SetWaitableTimer(hTimer, &dueTime, 20, NULL, NULL, FALSE); // could fail 

    HANDLE ah[2] = { hExitEvent, hTimer }; 
    bool exitLoop = false; 

    while(!exitLoop) 
    { 
     switch (WaitForMultipleObjects(2, ah, FALSE, INFINITE)) 
     { 
      default: // error would arrive here 
      case 0: exitLoop = true; break; 
      case 1: ExecuteMethod(); break; 
     } 
    } 
    timeEndPeriod(1); 
    CloseHandle(hTimer); 
    CloseHandle(hThread); 
    CloseHandle(hProcess); 
} 
Verwandte Themen