Why are asynchronous functions called twice?

I use a Threading timer to do a 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 an async timer callback and used WhenAll . In each working asynchronous function, I have one console output that shows activity. Now the problem is that in the second timer event, each asynchronous function works for some reason twice. The timer is set for a long period. An application is a type of Windows console. Is it Select that it runs twice?

+5
source share
3 answers

It:

 var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i)); 

creates a lazily evaluated IEnumerable that maps numbers to the results of a method call. ReadSensorsAsync is not called here, it will be called during the 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 called twice.


As csharpfolk suggested in the comments, IEnumerable materialization should fix this:

 var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i)).ToList(); 
+10
source

When you use Task.WhenAll in IEnumerable<Task<T>> , it will return T[] completed Task results. You need to save this variable and use it, otherwise you will get several enumerations, such as mentioned in his answer Henzi .

Here is a solution without calling too much .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); } 
+2
source

I think I know WHY! In a nutshell: - the reason that a function with impliciti expectation creates a callback thread. You can better see how to explain this to Jeffrey Richter in this video https://wintellectnow.com/Videos/Watch?videoId=performing-io-bound-asynchronous-operations from 00:17:25

just try:

 var tasksRead = Enumerable.Range(3, 35).Select(i => ReadSensorsAsync(i)); var tasksRecord = tasksRead.Where(x => x.Result != null).Select(x => RecordReadingAsync(x.Result)); await Task.WhenAll(tasksRead); await Task.WhenAll(tasksRecord); 
0
source

All Articles