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

dem*_*emo 14 c# lambda asynchronous timer async-await

我有异步回调,它被传递到Timer(来自System.Threading)构造函数:

private async Task HandleTimerCallback(object state)
{
    if (timer == null) return;

    if (asynTaskCallback != null)
    {
        await HandleAsyncTaskTimerCallback(state);
    }
    else
    {
        HandleSyncTimerCallback(state);
    }
}
Run Code Online (Sandbox Code Playgroud)

和计时器:

timer = new Timer(async o => await HandleTimerCallback(o), state, CommonConstants.InfiniteTimespan,
            CommonConstants.InfiniteTimespan);
Run Code Online (Sandbox Code Playgroud)

有没有办法o在lambda中省略那个参数?非同步的原因我可以将我handler作为委托传递

 timer = new Timer(HandleTimerCallback, state, CommonConstants.InfiniteTimespan,
            CommonConstants.InfiniteTimespan);
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 33

有没有办法在lambda中省略那个参数?

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

private async void HandleTimerCallback(object state)
Run Code Online (Sandbox Code Playgroud)

  • @Andy:我总是说要避免`async void`,因为它适用于事件处理程序.这是一个事件处理程序."async void"的异常处理语义旨在匹配事件的异常处理语义:在这种情况下,将在线程池线程上引发转义该方法的异常,就像该方法是同步的一样. (9认同)
  • 所以使用`async void`可以使用线程计时器吗?在你的许多文章中,你提到永远不要使用`async void`,因为异常永远不会被处理(除非特殊调用者调用该方法而不是处理这些场景,例如:WinForm事件) (3认同)
  • Stephen Cleary像Ironman一样处理异步/等待问题 (3认同)
  • @demo:因为 [`TimerCallback` 的返回类型为 `void`](https://msdn.microsoft.com/en-us/library/system.threading.timercallback(v=vs.110).aspx)。 (2认同)
  • @Vitalii 这与 async void 无关。这与定时器有关。如果您不希望重叠处理程序调用,请将 period 参数设置为 Timeout.Infinite。如果您确实想要一个重复的计时器,那么为了避免重叠调用,您必须使用锁来同步处理程序调用,或者在每次处理程序调用期间启动/停止计时器。 (2认同)

Vla*_*scu 10

您可以使用包装方法,如 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");
    }
}
Run Code Online (Sandbox Code Playgroud)


Ale*_*lex 8

我只是想提一下,.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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

你基本上会得到一个“永无止境的任务”。

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

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