-1

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

    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 帧)秒,这不是我的目标,我怀疑计时器正在等待事件完成其工作。这是真的吗,是否可以避免这种情况,所以无论事件是否完成,我都可以每毫秒开始发送帧?

4

2 回答 2

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;
            }
        }
    }
}
于 2019-09-11T12:39:32.637 回答
0

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

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

于 2019-09-11T11:49:49.223 回答