【问题标题】:c# run timer event every 1 milisecondc# 每 1 毫秒运行一次计时器事件
【发布时间】:2019-09-11 11:34:56
【问题描述】:

您好,我有一个问题,我有一个简单的定时事件,如下所示:

    public override async Task Execute(uint timedIntervalInMs = 1)
    {
        timer.Interval = timedInterval;
        timer.Elapsed += OnTimedEvent;
        timer.AutoReset = true;
        timer.Enabled = true;
    }

    protected override void OnTimedEvent(object source, ElapsedEventArgs evrntArgs)
    {            
        Task.Run(async () =>
        {
            var message = await BuildFrame();
            await sender.Send(message, null);
        });
    }

它的作用是构建大约 27 个字节的简单字节数组并通过 UDP 发送,我想每 1 毫秒发送一次该消息,但是当我使用计时器检查时,发送 1000 个请求大约需要 2-3 个(所以大约 330 帧每秒)秒,这不是我的目标,我怀疑计时器正在等待事件完成其工作。这是真的吗,是否可以避免这种情况,所以无论事件是否完成,我都可以每毫秒开始发送帧?

【问题讨论】:

  • 什么东西需要每秒更新 1000 次?,这似乎有点过分,甚至有点 DOS-ey。
  • 正是我需要它来进行设备压力测试。
  • 定时器没有这么高的分辨率,时间间隔以毫秒为单位,但实际上并没有这种能力。所以基本上这行不通。是否可以将一些消息打包以便平均达到目标速率?
  • 啊好吧,现在更有意义了。

标签: c#


【解决方案1】:

这样的东西可能非常有用,PeriodicYield<T> 函数将从生成器函数返回一系列结果。

这些结果将在尚未完成的最后一个完整周期结束时提供。

更改 SimpleGenerator 以模仿您想要的任何生成延迟。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace AsynchronouslyDelayedEnumerable
{
    internal class Program
    {
        private static int Counter;

        private static async Task Main(string[] args)
        {
            await foreach (var value in PeriodicYield(SimpleGenerator, 1000))
            {
                Console.WriteLine(
                    $"Time\"{DateTimeOffset.UtcNow}\", Value:{value}");
            }
        }

        private static async Task<int> SimpleGenerator()
        {
            await Task.Delay(1500);
            return Interlocked.Increment(ref Counter);
        }

        /// <summary>
        /// Yield a result periodically.
        /// </summary>
        /// <param name="generatorAsync">Some generator delegate.</param>
        /// <param name="periodMilliseconds">
        /// The period in milliseconds at which results should be yielded.
        /// </param>
        /// <param name="token">A cancellation token.</param>
        /// <typeparam name="T">The type of the value to yield.</typeparam>
        /// <returns>A sequence of values.</returns>
        private static async IAsyncEnumerable<T> PeriodicYield<T>(
            Func<Task<T>> generatorAsync,
            int periodMilliseconds,
            CancellationToken token = default)
        {
            // Set up a starting point.
            var last = DateTimeOffset.UtcNow;

            // Continue until cancelled.
            while (!token.IsCancellationRequested)
            {
                // Get the next value.
                var nextValue = await generatorAsync();

                // Work out the end of the next whole period.
                var now = DateTimeOffset.UtcNow;
                var gap = (int)(now - last).TotalMilliseconds;
                var head = gap % periodMilliseconds;
                var tail = periodMilliseconds - head;
                var next = now.AddMilliseconds(tail);

                // Wait for the end of the next whole period with
                // logarithmically shorter delays. 
                while (next >= DateTimeOffset.Now)
                {
                    var delay = (int)(next - DateTimeOffset.Now).TotalMilliseconds;
                    delay = (int)Math.Max(1.0, delay * 0.1);
                    await Task.Delay(delay, token);
                }

                // Check if cancelled.
                if (token.IsCancellationRequested)
                {
                    continue;
                }

                // return the value and update the last time.
                yield return nextValue;
                last = DateTimeOffset.UtcNow;
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    正如@harol 所说,Timer 没有这么高的分辨率。因为 Windows 或 Linux 不是实时操作系统。不可能在精确的时间触发事件。您可以在大约时间触发事件。

    操作系统或您的网卡驱动程序也可能决定等到网络缓冲区已满或达到特定值。

    【讨论】:

      猜你喜欢
      • 2014-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-18
      • 1970-01-01
      相关资源
      最近更新 更多