59

I have async callback, which is passed into Timer(from System.Threading) constructor :

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);

Is there any way to omit that o param in lambda? Cause for not-async I can just pass my handler as delegate

 timer = new Timer(HandleTimerCallback, state, CommonConstants.InfiniteTimespan,
            CommonConstants.InfiniteTimespan);
demo
  • 6,038
  • 19
  • 75
  • 149
  • 2
    It really doesn't make sense to do this mixing of Timer and async/await. If you want timer behaviour in async/await world, just make a loop with an `await Task.Delay(someValue)` somewhere in the loop body and call your method from the loop body too. – spender Aug 12 '16 at 12:28
  • 1
    sure it makes sense. maybe you want what the timer does to happen asynchronously so it does not block other actions – hal9000 Mar 02 '21 at 22:58

5 Answers5

116

Is there any way to omit that o param in lambda?

Sure, just define your event handler method as async void:

private async void HandleTimerCallback(object state)
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Do you mean without method body? And why not `Task` but `void` ? – demo Aug 12 '16 at 12:58
  • 2
    @demo: Because [`TimerCallback` has a return type of `void`](https://msdn.microsoft.com/en-us/library/system.threading.timercallback(v=vs.110).aspx). – Stephen Cleary Aug 12 '16 at 13:04
  • 20
    so using `async void` is OK with threaded timers? In many of your articles, you mention never to use `async void` due to exceptions never getting handled (unless the method is called by a special caller than can handle these scenarios, for example: WinForm events) – Andy Aug 26 '18 at 01:58
  • 42
    @Andy: I always say to avoid `async void` because it's intended for event handlers. This is an event handler. The exception handling semantics for `async void` are designed to match those for events: in this case, an exception escaping the method will be raised on a thread pool thread, just the same as if the method was synchronous. – Stephen Cleary Aug 26 '18 at 11:26
  • I've faced with problem that when starting using async Callback with that tricky async void. Timer triggers handlers many times instead of once, with interval repeating. – Vitalii Jan 23 '20 at 15:58
  • 2
    @Vitalii That has nothing to do with async void. That has to do with the Timer. If you don't want overlapping handler calls, then set the period parameter to Timeout.Infinite. If you do want a repeating timer, then to avoid overlapping calls you have to either use a lock to synchronize your handler calls, or start/stop the timer during each call of handler. – Neurion Jul 23 '20 at 03:04
  • @Vitalii Except `System.Threading.Timer` has no Stop or Start. – Yarek T Oct 25 '21 at 09:08
9

You could use a wrapper method, as recommended by David Fowler here:

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");
    }
}
Vlad Iliescu
  • 8,074
  • 5
  • 27
  • 23
4

I just wanted to mention that .NET 6 introduced a new timer class called PeriodicTimer that is async-first and avoids callbacks altogether.

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

Usage looks somewhat weird at first (because it's an endless loop), but since it's async, it does not block execution of other threads:

public async Task DoStuffPeriodically()
{
    var timer = new PeriodicTimer(TimeSpan.FromSeconds(10));

    while (await timer.WaitForNextTickAsync())
    {
        //do stuff
    }
}

It avoids callbacks altogether, uses simpler code and is a perfect candidate for background services.

You basically get a "never ending task" that does stuff.

To start the task simply call, for example _ = DoStuffPeriodically() with a discard operator (but do add try-catch inside the method so the background task does not crash) or launch this task via Task.Run

Nick Chapsas has a nice video explaining the usage: https://www.youtube.com/watch?v=J4JL4zR_l-0 (including how to use a CancellationToken to abort the timer).

Alex from Jitbit
  • 53,710
  • 19
  • 160
  • 149
-4

I used a wrapper like Vlad suggested above but then used a Task.Wait so that the asynchronous process completes:

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

protected async Task ProcessWorkAsync()
{
    
}
  • 1
    Why `Task.WaitAll(ProcessScheduledWorkAsync());` instead of `ProcessScheduledWorkAsync().Wait();`? And in either case, what benefit you get by blocking a thread? – Theodor Zoulias Feb 26 '21 at 15:39
  • @TheodorZoulias those are by far not the same, the second one can and most likely will cause threading-deadlocks. – Erik Philips Jun 01 '21 at 16:48
  • @ErikPhilips AFAIK these two are completely equivalent. Could you provide a minimal reproducible example that demonstrates the difference between `task.Wait()` and `Task.WaitAll(task)`? – Theodor Zoulias Jun 01 '21 at 17:59
  • @TheodorZoulias I'm aware how you think it works. I would suggest reading [A Tour of Task, Part 5: Waiting](https://blog.stephencleary.com/2014/10/a-tour-of-task-part-5-wait.html) and view the enormous SO Question [How to call asynchronous method from synchronous method in C#?](https://stackoverflow.com/questions/9343594/how-to-call-asynchronous-method-from-synchronous-method-in-c). Good Luck! – Erik Philips Jun 01 '21 at 18:20
  • @ErikPhilips from the [first link](https://blog.stephencleary.com/2014/10/a-tour-of-task-part-5-wait.html): *"The overloads for `WaitAll` are very similar to the overloads of `Wait`. [...] These are practically identical to `Task.Wait`, except they wait for multiple tasks to all complete."* So why do you think that the `Task.WaitAll(task)`, the trivial case where a single task argument is passed, is different from `task.Wait()`? – Theodor Zoulias Jun 01 '21 at 18:38
  • @TheodorZoulias Either you didn't care to read *However, most of the time, Task.Wait is dangerous because of its deadlock potential* or you're here to troll. Write code however you want. – Erik Philips Jun 02 '21 at 07:17
  • @ErikPhilips AFAIK the `task.Wait()` is as dangerous as the `Task.WaitAll(task)`. Please link to an authoritative document that explains the difference between these two methods, or provide a minimal and reproducible example that demonstrates the difference. Otherwise we are both losing our time, and no one gets wiser from this discussion. – Theodor Zoulias Jun 02 '21 at 07:28
  • @ErikPhilips you could ping Stephen Cleary and ask him to offer his opinion about this subject. I don't want to do it myself, because I am afraid that his time will be wasted too. But if you really believe that the `task.Wait()` is somehow more dangerous than the `Task.WaitAll(task)`, please do, and be ready to present your arguments. – Theodor Zoulias Jun 02 '21 at 07:42
-4

If you don't want a wrapper class, you can do this in your TimerCallback function:

private void TimerFunction(object state)
{
    _ = Task.Run(async () =>
    {
        await this.SomeFunctionAsync().ConfigureAwait(false);
    });
}
Quark Soup
  • 4,272
  • 3
  • 42
  • 74