16

是否有可能(甚至合理)进行System.Timers.Timer异步方法的回调?就像是:

var timer = new System.Timers.Timer
{
   Interval = TimeSpan.FromSeconds(30).TotalMilliseconds,
   AutoReset = true
};
timer.Elapsed += async (sender, e) => { /* await something */ };
timer.Start();

它编译(显然是一个很好的起点),但我不确定我是否理解后果。计时器会await在重置计时器之前回调吗?

4

4 回答 4

26

计时器会await在重置计时器之前回调吗?

不,没有什么可以等待的,因为 的签名ElapsedEventHandler有一个 void 返回类型。

换句话说,您的代码相当于:

var timer = new System.Timers.Timer { ... };
timer.Elapsed += Foo;
timer.Start();

...
private async void Foo()
{
    ...
}

这对您来说是否可以接受将取决于您的上下文。一般来说,拥有 async void 方法或匿名函数会使它们更难测试和重用 - 但正是为了事件处理程序而赋予了这种能力......你应该考虑错误将如何传播。

于 2015-01-27T18:16:37.600 回答
11

问题的标题是专门关于定时器的,但如果我们将其视为“如何在一段时间后调用异步方法?” 那么你可以在不使用计时器的情况下做到这一点。

var task2 = Task.Run(async () => {
    while (true)
    {
        try
        {
            await MyMethod2();
        } catch
        {
            //super easy error handling
        }
        await Task.Delay(TimeSpan.FromSeconds(5));
    }
});

...

public async Task MyMethod2()
{
    //async work here
}

但是请注意,这将有不同的时间(定时器将被调用间隔,上面的代码将被调用每个(运行时间 + sleep_time),但即使MyMethod2需要很长时间它也不会被调用两次。话虽如此那,您可以计算等待“每 x 分钟”运行多长时间。

于 2017-07-28T03:53:23.227 回答
-2

提出@tymtam 的解决方案不会等到MyMethod2结束。
我认为使用它会更好。
一个包含两个异步任务的示例,当两个任务都完成后,等待 5 秒并再次执行这两个任务:

var task2 = Task.Run(async () => {
    while (true)
    {
        try
        {
            var task1 = MyMethod1();
            var task2 = MyMethod2();
            List<Task> allTasks = new List<Task> { task1, task2 };
            while (allTasks.Count > 0)
            {
                Task finishedTask = await Task.WhenAny(allTasks);
                if (finishedTask == task1)
                {
                   Console.WriteLine("MyMethod1 has ended");
                }
                else if (finishedTask == task2)
                {
                   Console.WriteLine("MyMethod2 has ended");
                }
                tareas.Remove(finishedTask);
            }
            //Here only when finished all task
        } catch
        {
            //super easy error handling
        }
        //Wait until next cycle
        await Task.Delay(TimeSpan.FromSeconds(5));
    }
});

...

public async Task MyMethod1()
{
    //async work here
}

public async Task MyMethod2()
{
    //async work here
}
于 2021-08-01T13:31:37.967 回答
-2

事实上,你可以。

System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += async (x, y) => { await Task.Delay(1); };
于 2021-01-17T19:17:30.950 回答