System.Timers.Timer 的 Elapsed 回调可以是异步的吗?
Can the Elapsed callback of a System.Timers.Timer be async?
是否可以(甚至合理)使 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
重置定时器前的回调吗?
Will the timer await
the callback before resetting the timer?
没有。 可以 等待什么,因为 ElapsedEventHandler
的签名具有 void return 类型。
换句话说,您的代码相当于:
var timer = new System.Timers.Timer { ... };
timer.Elapsed += Foo;
timer.Start();
...
private async void Foo()
{
...
}
您是否可以接受这取决于您的背景。通常,使用异步 void 方法或匿名函数会使它们更难测试和重用 - 但这种能力正是为了事件处理程序而提供的......你应该考虑如何传播错误。
问题的标题专门针对定时器,但如果我们将其视为 "How to call an async method after some time?" 那么您可以在不使用定时器的情况下完成。
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
需要很长时间它不会被调用两次。话虽如此,你可以计算等待多长时间 运行 'every x minutes'.
其实可以。
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += async (x, y) => { await Task.Delay(1); };
@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
}
是否可以(甚至合理)使 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
重置定时器前的回调吗?
Will the timer
await
the callback before resetting the timer?
没有。 可以 等待什么,因为 ElapsedEventHandler
的签名具有 void return 类型。
换句话说,您的代码相当于:
var timer = new System.Timers.Timer { ... };
timer.Elapsed += Foo;
timer.Start();
...
private async void Foo()
{
...
}
您是否可以接受这取决于您的背景。通常,使用异步 void 方法或匿名函数会使它们更难测试和重用 - 但这种能力正是为了事件处理程序而提供的......你应该考虑如何传播错误。
问题的标题专门针对定时器,但如果我们将其视为 "How to call an async method after some time?" 那么您可以在不使用定时器的情况下完成。
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
需要很长时间它不会被调用两次。话虽如此,你可以计算等待多长时间 运行 'every x minutes'.
其实可以。
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += async (x, y) => { await Task.Delay(1); };
@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
}