在 C# 中以 10-15 毫秒的间隔记录来自结构的数据

Logging data from structs in interval 10-15 ms in C#

我必须准备 Logger class,它将以 10-15 毫秒的间隔从 3 个结构中保存数据。我解决这个问题的方法如下:

    public class Logger
{
    // Variables
    private Task loggerTask;
    public bool IsActive { get; set; }

    // Constructor
    public Logger()
    {

    }

    private void Logging()
    {
#if DEBUG
            System.Diagnostics.Debug.WriteLine("Logging has been started.");
#endif

        FileStream fs = new FileStream($"./log {DateTime.Now.ToString("dd.MM HH.mm.ss")}.txt", FileMode.Create, FileAccess.Write);
        StreamWriter sw = new StreamWriter(fs, Encoding.Default);

        try
        {
            Queue<double> times = new Queue<double>();
            Queue<Attitude> attitudes = new Queue<Attitude>();
            Queue<LocalPositionNed> positions = new Queue<LocalPositionNed>();
            Queue<SetPositionTargetLocalNed> setpoints = new Queue<SetPositionTargetLocalNed>();

            // Logs data
            DateTime start = DateTime.Now;
            DateTime last = start;
            DateTime now;

            while (IsActive)
            {
                now = DateTime.Now;

                if ((now - last).TotalMilliseconds < 16)
                    continue;

                last = now;
                
                times.Enqueue((now - start).TotalMilliseconds);
                attitudes.Enqueue(GCS.Instance.Drone.Attitude);
                positions.Enqueue(GCS.Instance.Drone.LocalPositionNed);
                setpoints.Enqueue(GCS.Instance.Offboard.SetPoint);
            }

            // Save data
            for(int i = 0; i < times.Count; i++)
            {
                sw.WriteLine($"{times.ElementAt(i)}\t" +
                    $"{attitudes.ElementAt(i).Pitch}\t" +
                    $"{attitudes.ElementAt(i).Roll}\t" +
                    $"{attitudes.ElementAt(i).Yaw}\t" +
                    $"{attitudes.ElementAt(i).Pitchspeed}\t" +
                    $"{attitudes.ElementAt(i).Rollspeed}\t" +
                    $"{attitudes.ElementAt(i).Yawspeed}\t" +
                    $"{positions.ElementAt(i).X}\t" +
                    $"{positions.ElementAt(i).Y}\t" +
                    $"{positions.ElementAt(i).Z}\t" +
                    $"{positions.ElementAt(i).Vx}\t" +
                    $"{positions.ElementAt(i).Vy}\t" +
                    $"{positions.ElementAt(i).Vz}\t" +
                    $"{setpoints.ElementAt(i).Vx}\t" +
                    $"{setpoints.ElementAt(i).Vy}\t" +
                    $"{setpoints.ElementAt(i).Vz}\t");
            }

        }
        catch (Exception ex)
        {
#if DEBUG
                System.Diagnostics.Debug.WriteLine($"Logging exception: {ex.Message}");
#endif
        }
        finally
        {
            sw.Dispose();
            fs.Dispose();
        }

#if DEBUG
            System.Diagnostics.Debug.WriteLine("Logging has been ended.");
#endif
    }

    // Static method
    public void Start()
    {
        IsActive = true;

        loggerTask = new Task(Logging);
        loggerTask.Start();
    }
    public void Stop()
    {
        IsActive = false;
    }
}

我对间隔有疑问,因为它们大约有 5-8 毫秒的变化。我的项目需要 1-2 毫秒的最大变化。有谁知道我可以如何改进我的方法。

感谢您的回复。

最大的问题可能是使用 DateTime.Now,分辨率很差,不适合这种任务。

一个更合适的简单替代方案是 stopwatch

        var delay = 16;
        var stopwatch = Stopwatch.StartNew();
        long current = 0;
        long previous;
        var next = stopwatch.ElapsedMilliseconds + delay;
        for (int i = 0; i < 100; i++)
        {

            while (next > stopwatch.ElapsedMilliseconds)
            {
                // Spin
            }

            next = stopwatch.ElapsedMilliseconds + delay;

            previous = current;
            current = stopwatch.ElapsedMilliseconds;
            var delta = current - previous;
            Console.WriteLine("Delta: " + delta);
        }

如评论中所述,这不提供任何强有力的保证,只是尽力而为。还有更好的方法可以做到这一点,不会浪费整个线程什么都不做,例如使用 multi media timers。但只要 CPU 上的负载很轻,它就可以满足 testing/debugging 的目的。