创建一个等待的System.Timers.Timer [英] Creating an awaitable System.Timers.Timer

查看:77
本文介绍了创建一个等待的System.Timers.Timer的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想到了创建一个可以等待的计时器,而不是引发事件.我还没有想到任何实际的应用程序,并且可能并不是非常有用的东西,但是我想看看它是否至少可以作为练习来使用.这是可以使用的方式:

I had an idea about creating a timer that can be awaited, instead of raising events. I haven't thought of any practical applications yet, and may not be something terribly useful, but I would like to see if it's at least doable as an exercise. This is how it could be used:

var timer = new System.Timers.Timer();
timer.Interval = 100;
timer.Enabled = true;
for (int i = 0; i < 10; i++)
{
    var signalTime = await timer;
    Console.WriteLine($"Awaited {i}, SignalTime: {signalTime:HH:mm:ss.fff}");
}

等待计时器10次,预期输出为:

The timer is awaited 10 times, and the expected output is:

等待0,SignalTime:06:08:51.674
等待1,SignalTime:06:08:51.783
等待2,SignalTime:06:08:51.891
等待3,SignalTime:06:08:52.002
等待4,SignalTime:06:08:52.110
等待5,SignalTime:06:08:52.218
等待6,SignalTime:06:08:52.332
等待7,SignalTime:06:08:52.438
等待8,SignalTime:06:08:52.546
等待9,SignalTime:06:08:52.660

Awaited 0, SignalTime: 06:08:51.674
Awaited 1, SignalTime: 06:08:51.783
Awaited 2, SignalTime: 06:08:51.891
Awaited 3, SignalTime: 06:08:52.002
Awaited 4, SignalTime: 06:08:52.110
Awaited 5, SignalTime: 06:08:52.218
Awaited 6, SignalTime: 06:08:52.332
Awaited 7, SignalTime: 06:08:52.438
Awaited 8, SignalTime: 06:08:52.546
Awaited 9, SignalTime: 06:08:52.660

在这种情况下,简单的await Task.Delay(100)会执行相同的操作,但是计时器提供了控制程序另一部分之间的时间间隔的灵活性(可能的

In this case a simple await Task.Delay(100) would do the same thing, but a timer gives the flexibility of controlling the interval from another part of the program (with the caveat of possible thread safety issues).

关于实现,我发现一篇文章,其中描述了如何进行各种等待的事物,例如TimeSpanintDateTimeOffsetProcess.看来我必须编写一个返回TaskAwaiter的扩展方法,但是我不确定该怎么做.有人知道吗?

Regarding the implementation, I found an article that describes how to make various things awaitable, like a TimeSpan, an int, a DateTimeOffset and a Process. It seems that I must write an extension method that returns a TaskAwaiter, but I am not sure what to do exactly. Does anyone has any idea?

public static TaskAwaiter GetAwaiter(this System.Timers.Timer timer)
{
    // TODO
}


更新:我使用了接受的答案.


Update: I updated the example code and the expected output, using actual output from the execution of the accepted answer.

推荐答案

似乎我必须编写一个扩展方法来返回TaskAwaiter,但是我不确定该怎么做.

It seems that I must write an extension method that returns a TaskAwaiter, but I am not sure what to do exactly.

返回等待者的最简单方法是获取Task,然后在其上调用GetAwaiter.您还可以创建自定义的等待者,但这涉及更多.

The easiest way to return an awaiter is to get a Task and then call GetAwaiter on it. You can also create custom awaiters, but that's much more involved.

因此问题变成事件引发后如何完成任务?"答案是

So the question becomes "how do I get a task that is completed when an event is raised?" And the answer to that is to use TaskCompletionSource<T>:

public static class TimerExtensions
{
    public static Task<DateTime> NextEventAsync(this Timer timer)
    {
        var tcs = new TaskCompletionSource<DateTime>();
        ElapsedEventHandler handler = null;
        handler = (_, e) =>
        {
            timer.Elapsed -= handler;
            tcs.TrySetResult(e.SignalTime);
        };
        timer.Elapsed += handler;
        return tcs.Task;
    }

    public static TaskAwaiter<DateTime> GetAwaiter(this Timer timer)
    {
        return timer.NextEventAsync().GetAwaiter();
    }
}

因此,这将使您的示例代码按预期工作.但是,有一个重要警告:每个await都将调用GetAwaiter,该订阅下一个Elapsed事件.并且Elapsed事件处理程序在await完成之前被删除.因此,从事件触发到下一次等待计时器开始,没有 处理程序,而且您使用的代码很容易错过某些事件.

So, that will make your sample code work as expected. However, there is a significant caveat: each await will call GetAwaiter, which subscribes to the next Elapsed event. And that Elapsed event handler is removed before the await completes. So from the time the event fires until the next time the timer is awaited, there is no handler, and your consuming code can easily miss some events.

如果这是不可接受的,则应使用围绕订阅-然后-接收-事件模型设计的IObservable<T>,或使用类似Channels的缓冲事件并通过异步流使用的事件.

If this is not acceptable, then you should use IObservable<T>, which is designed around a subscription-then-receive-events model, or use something like Channels to buffer the events and consume them with an asynchronous stream.

这篇关于创建一个等待的System.Timers.Timer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆