Кроссплатформенный тиковый счетчик высокого разрешения на Mono? - PullRequest
2 голосов
/ 12 октября 2011

Я ищу счетчик тиков высокого разрешения на Mono, предпочтительно примерно того же разрешения, что и QueryPerformanceCounter на Win32 / .NET.

Это то, что нужно реализовать как собственный вызов (напримерQueryPerformanceCounter находится на .NET / Win32) на каждой платформе, которую мне нужно поддерживать?(Linux, OSX).

Мне нужно разрешение <1 мс. </p>

Ответы [ 3 ]

2 голосов
/ 12 октября 2011

Для этого вы должны использовать System.Diagnostics.Stopwatch.

1 голос
/ 14 июля 2017

И вот мой ответ здесь Таймер высокого разрешения

https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545

  1. Работает на всех платформах (поэтому нет using Mono.Unix.Native;) и имеет высокую точность везде, где StopWatch.IsHighPrecision == true

  2. Его Elapsed событие гарантированно не перекрывается (что может быть важно знать, поскольку изменения состояния внутри обработчика события могут остатьсянезащищенный от многопоточного доступа)

Вот как его использовать:

Console.WriteLine($"IsHighResolution = {HighResolutionTimer.IsHighResolution}");
Console.WriteLine($"Tick time length = {HighResolutionTimer.TickLength} [ms]");

var timer = new HighResolutionTimer(0.5f);

// UseHighPriorityThread = true, sets the execution thread 
// to ThreadPriority.Highest.  It doesn't provide any precision gain
// in most of the cases and may do things worse for other threads. 
// It is suggested to do some studies before leaving it true
timer.UseHighPriorityThread = false;

timer.Elapsed += (s, e) => { /*... e.Delay */ }; // The call back
timer.Start();  
timer.Stop();    // by default Stop waits for thread.Join()
                 // which, if called not from Elapsed subscribers,
                 // would mean that all Elapsed subscribers
                 // are finished when the Stop function exits 
timer.Stop(joinThread:false)   // Use if you don't care and don't want to wait

Вот эталонный тест (и пример в реальном времени):
https://gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3

Результаты установки таймера на 0,5 мс в Windows 10: enter image description here

Также стоит отметить, что:

  1. У меня была такая же точность на моно на Ubuntu.

  2. При игре с эталоном максимальное и очень редкое отклонение, которое я видел, составляло около 0,5 мс (что, вероятно, ничего не значит, это не системы реального времени, но все же стоит упомянуть)

  3. Тики секундомера не являются тиками TimeSpan. На этом компьютере Windows 10 HighResolutionTimer.TickLength равно 0,23 [нс].

1 голос
/ 17 июня 2016

Смотрите мой ответ здесь: https://stackoverflow.com/a/37882723/5073786

using Mono.Unix.Native;
namespace drone.StackOverflow{

  internal class LinuxHiResTimer {
    internal event EventHandler Tick; // Tick event 

    private System.Diagnostics.Stopwatch watch; // High resolution time
    const uint safeDelay = 0; // millisecond (for slightly early wakeup)
    private Timespec pendingNanosleepParams = new Timespec();
    private Timespec threadNanosleepParams = new Timespec();
    object lockObject = new object();
    internal long Interval { 
        get{
            double totalNanoseconds;
            lock (lockObject) {
                totalNanoseconds= (1e9 * pendingNanosleepParams.tv_sec)
                                         + pendingNanosleepParams.tv_nsec; 


            }
            return (int)(totalNanoseconds * 1e-6);//return value in ms
        } 
        set{
            lock (lockObject) {
                pendingNanosleepParams.tv_sec = value / 1000;
                pendingNanosleepParams.tv_nsec = (long)((value % 1000) * 1e6);//set value in ns
            }
        }
    }
    private bool enabled;
    internal bool Enabled {
        get { return enabled; }
        set {
            if (value) {
                watch.Start();
                enabled = value;
                Task.Run(()=>tickGenerator()); // fire up new thread
            }
            else {
                lock (lockObject) {
                    enabled = value;
                }
            }
        }

    }
    private Task tickGenerator() {
        bool bNotPendingStop; 
        lock (lockObject) {
            bNotPendingStop = enabled;
        }
        while (bNotPendingStop) {
            // Check if thread has been told to halt

            lock (lockObject) {
                bNotPendingStop = enabled;
            }
            long curTime = watch.ElapsedMilliseconds;
                if (curTime >= Interval) {
                    watch.Restart ();
                    if (Tick != null)
                        Tick (this, new EventArgs ());
                } else {
                    long iTimeLeft = (Interval - curTime); // How long to delay for 
                    if (iTimeLeft >= safeDelay) { // Task.Delay has resolution 15ms//await Task.Delay(TimeSpan.FromMilliseconds(iTimeLeft - safeDelay));
                        threadNanosleepParams.tv_nsec = (int)((iTimeLeft - safeDelay) * 1e6);
                        threadNanosleepParams.tv_sec = 0;
                        Syscall.nanosleep (ref threadNanosleepParams, ref threadNanosleepParams);
                    }
                }

        }
        watch.Stop();
        return null;
    }
}

Использование:

 private myMainFunction(){
  LinuxHiResTimer timReallyFast = new LinuxHiResTimer();
  timReallyFast.Interval=25; // 
  timReallyFast.Tick += new EventHandler(timReallyFast_Tick);
  timReallyFast.Enabled = true;
}
private void timReallyFast_Tick(System.Object sender, System.EventArgs e) {
// Do this quickly i.e. 
 PollSerialPort();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...