65

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); 
2
  • 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. Commented Aug 12, 2016 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 Commented Mar 2, 2021 at 22:58

5 Answers 5

128

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) 
Sign up to request clarification or add additional context in comments.

7 Comments

Do you mean without method body? And why not Task but void ?
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: 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.
@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.
|
18

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 timer simply start the task without awaiting. For example _ = DoStuffPeriodically() with a discard operator or launch this task via Task.Run. Remember to add try-catch inside the method so the background task does not crash.

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

Comments

12

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

6 Comments

This is only good if you are OK with firing and forgetting tasks (I.e. ignoring their possible errors).
@TheodorZoulias But isn't async void approach in the accepted answer also a "fire and forget"?
@Alex no, async void is fire-and-crash, not fire-and-forget. Any error in an async void method is rethrown on the captured synchronization context, which usually terminates the process.
@TheodorZoulias Lucian Wischik has interesting (old) video's on the topic and the description of the first says "Async void is a "fire-and-forget" mechanism". Check it out here learn.microsoft.com/en-us/shows/three-essential-tips-for-async/…
@EmielKoning good find. Quoting from that video (​19:40) "Fire-and-forget means that the caller is unable to know when an async void is finished, and is unable to catch exceptions that get thrown from an async void method or lambda."
|
-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() { } 

8 Comments

Why Task.WaitAll(ProcessScheduledWorkAsync()); instead of ProcessScheduledWorkAsync().Wait();? And in either case, what benefit you get by blocking a thread?
@TheodorZoulias those are by far not the same, the second one can and most likely will cause threading-deadlocks.
@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)?
@TheodorZoulias I'm aware how you think it works. I would suggest reading A Tour of Task, Part 5: Waiting and view the enormous SO Question How to call asynchronous method from synchronous method in C#?. Good Luck!
@ErikPhilips from the first link: "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()?
|
-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); }); } 

1 Comment

This is doing the same as the wrapper.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.