将异步回调传递给Timer构造函数

本文关键字:Timer 构造函数 异步 回调 | 更新日期: 2023-09-27 18:02:13

我有一个异步回调,它被传递给Timer(从System.Threading)构造函数:

private async Task HandleTimerCallback(object state)
{
    if (timer == null) return;
    if (asynTaskCallback != null)
    {
        await HandleAsyncTaskTimerCallback(state);
    }
    else
    {
        HandleSyncTimerCallback(state);
    }
}

And Timer:

timer = new Timer(async o => await HandleTimerCallback(o), state, CommonConstants.InfiniteTimespan,
            CommonConstants.InfiniteTimespan);

在lambda中是否有办法省略o参数?因为对于非异步,我可以将handler作为委托传递

 timer = new Timer(HandleTimerCallback, state, CommonConstants.InfiniteTimespan,
            CommonConstants.InfiniteTimespan);

将异步回调传递给Timer构造函数

是否有任何方法可以省略lambda中的0参数?

当然,只需将事件处理程序方法定义为async void:

private async void HandleTimerCallback(object state)

您可以使用包装器方法,就像David Fowler在这里推荐的那样:

public class Pinger
{
    private readonly Timer _timer;
    private readonly HttpClient _client;
    
    public Pinger(HttpClient client)
    {
        _client = client;
        _timer = new Timer(Heartbeat, null, 1000, 1000);
    }
    public void Heartbeat(object state)
    {
        // Discard the result
        _ = DoAsyncPing();
    }
    private async Task DoAsyncPing()
    {
        await _client.GetAsync("http://mybackend/api/ping");
    }
}

我只是想提一下。net 6引入了一个名为PeriodicTimer的新定时器类,它是异步优先的,并且完全避免了回调。

https://learn.microsoft.com/en-us/dotnet/api/system.threading.periodictimer

的用法一开始看起来有点奇怪(因为它是一个无限循环),但由于它是异步的,它不会阻塞其他线程的执行:

public async Task DoStuffPeriodically()
{
    var timer = new PeriodicTimer(TimeSpan.FromSeconds(10));
    while (await timer.WaitForNextTickAsync())
    {
        //do stuff
    }
}

它完全避免了回调,使用更简单的代码,是后台服务的完美候选。

你会得到一个"永不结束的任务"

要启动任务,只需调用_ = DoStuffPeriodically(),例如使用丢弃操作符(但要在方法中添加try-catch,以便后台任务不会崩溃)或通过Task.Run启动该任务

Nick Chapsas有一个很好的视频解释使用:https://www.youtube.com/watch?v=J4JL4zR_l-0(包括如何使用CancellationToken中止定时器)。

我使用了上面建议的Vlad包装器,但随后使用了Task。等待异步进程完成:

private void ProcessWork(object state)
{
    Task.WaitAll(ProcessScheduledWorkAsync());
}
protected async Task ProcessWorkAsync()
{
    
}

如果您不想使用包装器类,您可以在TimerCallback函数中这样做:

private void TimerFunction(object state)
{
    _ = Task.Run(async () =>
    {
        await this.SomeFunctionAsync().ConfigureAwait(false);
    });
}