I'm using Threading timer to do some periodic job:
private static async void TimerCallback(object state)
{
if (Interlocked.CompareExchange(ref currentlyRunningTasksCount, 1, 0) != 0)
{
return;
}
var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i));
await Task.WhenAll(tasksRead);
var tasksRecord = tasksRead.Where(x => x.Result != null).Select(x => RecordReadingAsync(x.Result));
await Task.WhenAll(tasksRecord);
Interlocked.Decrement(ref currentlyRunningTasksCount);
}
I made timer call back async
and used WhenAll
. In each working async function I have one Console output, which shows activity. Now the problem is that on second timer event each async function is working twice for some reason. The timer is set to long period. The application is Windows Console type. Is it Select
that somehow make it run twice?
Async functions will always return a value. It makes sure that a promise is returned and if it is not returned then JavaScript automatically wraps it in a promise which is resolved with its value. Example-1: javascript.
Note: The purpose of async / await is to simplify the syntax necessary to consume promise-based APIs. The behavior of async / await is similar to combining generators and promises. Async functions always return a promise.
Async/await and then() are very similar. The difference is that in an async function, JavaScript will pause the function execution until the promise settles. With then() , the rest of the function will continue to execute but JavaScript won't execute the . then() callback until the promise settles.
Using one try/catch block containing multiple await operations is fine when waiting for promises created on the right hand side of the await unary operator: The await operator stores its parent async functions' execution context and returns to the event loop.
This:
var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i));
creates a lazily evaluated IEnumerable which maps numbers to method invocation results. ReadSensorsAsync
is not invoked here, it will be invoked during evaluation.
This IEnumerable is evaluated twice. Here:
await Task.WhenAll(tasksRead);
and here:
// Here, another lazy IEnumerable is created based on tasksRead.
var tasksRecord = tasksRead.Where(...).Select(...);
await Task.WhenAll(tasksRecord); // Here, it is evaluated.
Thus, ReadSensorsAsync
is invoked twice.
As csharpfolk suggested in the comments, materializing the IEnumerable should fix this:
var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i)).ToList();
When you use Task.WhenAll
on a IEnumerable<Task<T>>
it will return a T[]
of the completed Tasks results. You need to save that variable and use it or else you will end up with the multiple enumerations like Henzi mentioned in his answer.
Here is a solution without the unnecessarily calling of .ToList()
private static async void TimerCallback(object state)
{
if (Interlocked.CompareExchange(ref currentlyRunningTasksCount, 1, 0) != 0)
{
return;
}
var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i));
var finshedTasks = await Task.WhenAll(tasksRead);
var tasksRecord = finshedTasks.Where(x => x != null).Select(x => RecordReadingAsync(x));
await Task.WhenAll(tasksRecord);
Interlocked.Decrement(ref currentlyRunningTasksCount);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With