Async Await Few Confusions - c#

As experiencing the new async & Await features of 4.5 I want to clear some confusions before going any further. I have been reading different article and also different question on SO and it help me undertands how Async and Await works. I will just try to put my understanding and confusions here and will appreciate if someone code educate me and other people who are looking for same things. I am discussing this in very simple wordings.
So Async is used so that compiler know that method marked by Async contains Await operation (Long operation). Latest framework contains different new builtin methods for Async Operations.
The builtin Async functions like connection.OpenAsync, ExecuteScalarAsync etc are used with Await keyword. I don't know the inner working of these Async Methods but my strong guess is that under the hood they are using Tasks.
Can I make this as general rule that Await will be with any method which implements Task. So if I need to create my own method which is performing long operation then will I create it as Task and when it is called I will use Await Keyword with it?
Second most important thing is that what is the rule of thumb of creating a method as Async or creating it as task. For example,
public void SampleMain()
{
for (int i = 1; i <= 100; i++)
{
DataTable dt = ReadData(int id);
}
}
public DataTable ReadData(int id)
{
DataTable resultDT = new DataTable();
DataTable dt1 = new DataTable();
// Do Operation to Fill DataTable from first connection string
adapter.Fill(dt1);
DataTable dt2 = new DataTable();
// Do Operation to Fill DataTable from first connection string
adapter.Fill(dt2);
// Code for combining datatable and returning the resulting datatable
// Combine DataTables
return resultDT;
}
public string GetPrimaryConnectionString()
{
// Retrieve connection string from some file io operations
return "some primary connection string";
}
public string GetSecondaryConnectionString()
{
// Retrieve connection string from some file io operations
return "some secondaryconnection string";
}
Now this is a very simple scenario that I have created based on some real world application I worked in past. So I was just wondering how to make this whole process Async.
Should I make GetPrimaryConnectionString and GetSecondaryConnectionString as Tasks and Await them in ReadData. Will ReadData be also a Task? How to call ReadData in the SampleMain function?
Another way could be to create a Task for ReadData in SampleMain and run that Task and skip converting other methods as Task. Is this the good approach? Will it be truly Asynchronous?

So Async is used so that compiler know that method marked by Async
contains Await operation
The async is used so that the compiler will have an indication to create a state-machine out of the method. An async method can have no await, and still work, though it will execute completely synchronously.
The builtin Async functions like connection.OpenAsync,
ExecuteScalarAsync etc are used with Await keyword. I don't know the
inner working of these Async Methods but my strong guess is that under
the hood they are using Tasks.
Task is a promise of work to be completed in the future. There are a couple of ways to create a Task. But, Task isn't the only thing that can be represent an asynchronous operation. You can create an awaitable yourself if you wanted, all it needs it to implement a GetAwaiter method which returns a type implementing INotifyCompletion.
If you want to know how a method is implemented in the framework, you can view the source. In this particular case, they use TaskCompletionSource<T>.
Should I make GetPrimaryConnectionString and
GetSecondaryConnectionString as Tasks and Await them in ReadData. Will
ReadData be also a Task? How to call ReadData in the SampleMain
function?
There is nothing inherently asynchronous about retrieving a connection string. You usually (not always) use async-await with naturally async IO operations. In this particular case, the only actual async operation is ReadData, and if you want to make it asynchronous, you can use SqlDataReader, which exposes async methods.
An example, taken from the ADO.NET teams blog:
public static async Task<Product> GetProductAndReviewsAsync(
int productID, int reviewsToGet)
{
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
await connection.OpenAsync();
const string commandString = GetProductByIdCommand + ";"
+ GetProductReviewsPagedById;
using (SqlCommand command = new SqlCommand(commandString, connection))
{
command.Parameters.AddWithValue("productid", productID);
command.Parameters.AddWithValue("reviewStart", 0);
command.Parameters.AddWithValue("reviewCount", reviewsToGet);
using (SqlDataReader reader = await command.ExecuteReaderAsync())
{
if (await reader.ReadAsync())
{
Product product = GetProductFromReader(reader, productID);
if (await reader.NextResultAsync())
{
List<Review> allReviews = new List<Review>();
while (await reader.ReadAsync())
{
Review review = GetReviewFromReader(reader);
allReviews.Add(review);
}
product.Reviews = allReviews.AsReadOnly();
return product;
}
else
{
throw new InvalidOperationException(
"Query to server failed to return list of reviews");
}
}
else
{
return null;
}
}
}
}
}

how to make this whole process Async
If there are asynchronous versions of adapter.Fill, then simply await for it in ReadData, which in turn also become async and you can await for it in the caller method:
// in async button click event
button.Enabled = false;
var dt = await ReadData(int id);
button.Enabled = true;
... // do something with dt
public async Task<DataTable> ReadData(int id)
{
...
var job1 = adapter.AsyncFill(dt1);
var job2 = adapter.Fill(dt2);
// wait for all of them to finish
Task.WaitAll(new[] {job1, job2});
...
return Task.FromResult(resultDT); // dump approach
}
If there are no asynchronous version then you have to create them (by using Task):
// in async button click event
button.Enabled = false;
// run synchronous task asynchronously
var dt = await Task.Run(() => ReadData(int id));
button.Enabled = true;
... // do something with dt
async/await shines when it comes to UI, otherwise (if no UI is involved) just create task and run synchronous operation there.

The only reason to use async-await is if your main thread might do something useful while another thread is doing the length operation. If the main thread would start the other thread and only wait for the other thread to finish, it is better to let the main thread do the action.
One of the things a main thread quite often does is keep the UI responsive.
You are right, under the hood async-await uses Task, hence you see that an async function returns a Task.
The rules:
If a function would return void, the async version returns Task. If the function would return TResult, the async version should return Task<TResult>.
There is one exception: the async event handler returns void.
The return value of await Task is void. The return value of await Task<TResult> is TResult.
Only async functions can call other async functions.
If you have a non-async function you can still use the async function. However you cannot use await. Use the Task return value of the async function and the System.Threading.Tasks.Task methods to wait for the results.
If you have an async function and want to start a non-async function in a separate thread, use:
private int SlowCalculation(int a, int b)
{
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return a + b;
}
private async Task CalculateAsync(int a, int b)
{
Task myTask = Task.Run( () => SlowCalculation(a, b);
// while SlowCalcuation is calculating slowly, do other useful things
// after a while you need the answer
int sum = await myTask;
return sum;
}
See that the return of await Task<int> is int.
Some people used to use functions like Task.ContinueWith. Because of the await statement that is not needed anymore. Await makes sure that the task is finished. The statement after the await is what you'd normally do in the ContinueWith.
In Task.ContinueWith you could say: "do this only if the task failed". The async-await equivalent for this is try-catch.
Remember: if your thread has nothing useful to do (like keeping your UI responsive), don't use async-await
Starting several tasks in async-await and waiting for them to finish is done as follows:
private async Task MyAsyncFunction(...)
{
var tasks = new List<Task<int>>();
for (int i=0; i<10; ++i)
{
tasks.Add(CalculateAsync(i, 2*i);
}
// while all ten tasks are slowly calculating do something useful
// after a while you need the answer, await for all tasks to complete:
await Task.WhenAll(tasks);
// the result is in Task.Result:
if (task[3].Result < 5) {...}
}
The async-await version of Task.Waitall is Task.WhenAll. WhenAll returns a Task instead of void, so you can await for it. The main thread remains responsive even while awaiting.
The main thread is not the case when using Task.WaitAll, because you don't await.

Related

Can I run sync code as async to gain performance? [duplicate]

This question already has answers here:
Why use async and return await, when you can return Task<T> directly?
(9 answers)
Closed 2 years ago.
I've read a bunch of forums, tutorials and blogs talking about the usage of Async/Await in C#. The more I read the more confusing it gets.
Also, people mainly talk about calling async stuff in a sync method but not calling sync stuff in an async method.
As I am a junior developer and do not have much experience with async programming I'll post a new question here and hope for some enlightenment.
Consider this:
I have a Web API endpoint that does some calculations and model building and returns some data.
public async Task<JsonResult> GetData()
{
Task<Data> stuff1Task = CalculateStuff1();
Task<Data> stuff2Task = CalculateStuff2();
Task<Data> stuff3Task = CalculateStuff3();
return Json(
new
{
stuff1 = await stuff1Task,
stuff2 = await stuff2Task,
stuff3 = await stuff3Task
}, JsonRequestBehavior.AllowGet
);
}
private async Task<Data> CalculateStuff1()
{
return await SomeAsyncCalculation();
}
private async Task<Data> CalculateStuff2()
{
return SomeSyncCalculation();
}
private async Task<Data> CalculateStuff3()
{
Task<Data> dataTask1 = SomeAsyncCalculation();
Task<Data> dataTask2 = AnotherAsyncCalculation();
Data data1 = await dataTask1;
Data data2 = await dataTask2;
Data combindedData = SyncMethodToCombineData(data1, data2);
return combindedData;
}
Why I consider mixing async and sync code is for getting better performance.
In this case lets pretend SomeAsyncCalculation(), SomeSyncCalculation() and AnotherAsyncCalculation() are pretty costly methods. My goal is to get the methods to run somewhat in parallel to gain some faster response times.
I know it is best to go "Async all the way" but lets be real, rebuilding half the project is not always a priority or a possibility.
Also I might have some integrations with other systems that do not support async operations.
This warning I get for CalculateStuff2() adds to the confusion. :
this async method lacks 'await' operators and will run synchronously
In my understanding the "async" keyword is only good for wrapping the method and allowing me to use await keyword. It also allows me to just return the data and I don't need to manage Task returning results. It also handles exceptions.
The Task<TResult> return type is what makes the method execute on a different thread (although it is not guaranteed it will execute on a different thread).
Concluding questions:
1. Will the async method that does not use await (CalculateStuff2()) run synchronously on it's own thread (if it runs on another thread because it is a Task) or will it run in the main thread of the API call, and always block it no matter what?
2. Is it bad practice to use async without await just to have a nicely wrapped task method out of the box?
You're not need for async in sync method. async generates State Machine that is a kind of redundancy in case you're not need for await.
Consider this somewhat optimized example.
public async Task<JsonResult> GetData()
{
Task<Data> stuff1Task = CalculateStuff1();
Task<Data> stuff3Task = CalculateStuff3();
Data stuff2data = CalculateStuff2(); // run sync method after launching async ones
return Json(new
{
stuff1 = await stuff1Task,
stuff2 = stuff2data,
stuff3 = await stuff3Task
}, JsonRequestBehavior.AllowGet);
}
private Task<Data> CalculateStuff1() // optimized
{
return SomeAsyncCalculation();
}
private Data CalculateStuff2()
{
return SomeSyncCalculation();
}
private async Task<Data> CalculateStuff3()
{
//use combinator to simplify the code
Data[] data = await Task.WhenAll(SomeAsyncCalculation(), AnotherAsyncCalculation());
Data combindedData = SyncMethodToCombineData(data[0], data[1]);
return combindedData;
}
Also consider to differ the CPU-bound and IO-bound operations, look at this article. There's different async approach depending on what exacly you're launching.
Direct answers
Will the async method that does not use await (CalculateStuff2()) run synchronously on it's own thread (if it runs on another thread because it is a Task) or will it run in the main thread of the API call, and always block it no matter what?
Yes, it will run synchronously on the caller Thread. If you want to run some sync method on its own Thread, use Task.Run():
Task<Data> stuff2Task = Task.Run(() => CalculateStuff2());
and then await it.
Is it bad practice to use async without await just to have a nicely wrapped task method out of the box?
Yes, it's bad practice. Redundant State Machine makes overhead which in this case is worthless.

Why await an async function directly not work without assigning it to a Task variable

Typically, I do the following
public static async Task dosth()
{
List<Task> job = new List<Task>();
for (int i = 0; i < 3; i++)
{
job.Add(sleep());
}
Task.WhenAll(job.ToArray());
}
static async Task sleep()
{
await Task.Delay(1000);
Console.WriteLine("Finish new");
}
It works smoothly, no problem. But when I do a review on my own code (trying using other syntax to do the same job), I suddenly figure out the following two are different.
public static async Task dosthA()
{
//This will be working synchronously, take 3 seconds.
await sleep();
await sleep();
await sleep();
//This will be working asynchronously, take 1 second only.
Task A = sleep();
Task B = sleep();
Task C = sleep();
await A;
await B;
await C;
}
Why assigning the async function to a new variable make difference? I originally think they are the same.
Update
Why it is confusing me is, actually in Microsoft doc on Async-await,
They stated the following in their code.
// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();
They are actually different, why they use //or , in a single statement, just because it makes no different in their own example?
This is because when you are returning a running Task when you call Sleep() even when you're assigning to a variable.
The confusion is that the Task does not begin if you assign it to a variable (A, B, or C) until you call await A; but that's not true. As soon as you assign sleep(); to A, sleep() was called; therefore the Task in the sleep() method is running. Assigning it to a variable or not the Task begins when you call the method; because in the method you start the Task.
Knowing this; when you call:
await A;
await B;
await C;
A, B, and C, have already starting simultaneously... After awaiting A it is most likely B, and C have also completed or are milliseconds from completing.
There are situations where you can reference a Task that hasn't started yet but you would have to purposely return a non-running Task to do that.
To answer the edit to your question also.
Tasks have a method called GetAwaiter() which returns a TaskAwaiter. In C# when you write var task = sleep(); then you're assigning the actual Task to the task variable. All the same when you write await sleep(); the compiler does some cool stuff and it actually calls the Task.GetAwaiter() method; which is subscribed to. The Task will run and when it is complete the TaskAwaiter fires the continuation action. This can't be explained in a simple answer but to know the outer logic helps.
Among other things the TaskAwaiter implements ICriticalNotifyCompletion which in turn implements INotifyCompletion. Both have one method each, OnCompleted(Action) and UnsafeOnCompleted(Action) (you can guess which is which by naming convention).
Another thing to note is that Task.GetAwaiter() returns a TaskAwaiter but Task<TResult>.GetAwaiter() returns a TaskAwaiter<TResult>. There's not a strong difference in the two but there is a difference in the GetResult() method of the two tasks; which is what's called while marshalling back to the proper threading context. The TaskAwaiter.GetResult() returns void and the TaskAwaiter<TResult>.GetResult() returns TResult.
I feel like if I push further into this I'll have to write pages to explain it all in detail... Hopefully just explaining your question and pulling the curtain back a little bit will shed enough light to help you both understand and dig deeper if you're more curious.
Ok, so based on the comment below I want to describe my answer a little bit further.
I'll start this simple; let's just make a Task; one that isn't running, and look at it first.
public Task GetTask()
{
var task = new Task(() => { /*some work to be done*/ });
//Now we have a reference to a non-running task.
return task;
}
We can now call code like:
public async void DoWork()
{
await GetTask();
}
… but we'll be waiting forever; until the application ends, because the Task was never started. However; we could do something like this:
public async void DoWork()
{
var task = GetTask();
task.Start();
await task;
}
… and it will await the running Task and continue once the Task is complete.
Knowing this you can make as many calls to GetTask() as you like and you'll only be referencing Tasks that have not started.
In your code it's just the opposite, which is fine, as this is the most used way. I encourage you to make sure your method names notify the user of how you're returning the Task. If the Task is already running the most common convention is the end the method name with Async. Here's another example doing it with a running Task for clarity.
public Task DoTaskAsync()
{
var task = Task.Run(() => { /*some work to be done*/ });
//Now we have a reference to a task that's already running.
return task;
}
And now we will most likely call this method like:
public async void DoWork()
{
await DoTaskAsync();
}
However; note that if we simply want to reference the Task just like we did earlier, we can, the only difference is this Task is running where the one prior was not. So this code is valid.
public async void DoWork()
{
var task = DoTaskAsync();
await task;
}
The big take away is how C# handles the async / await keywords. async tells the compiler that the method is going to become a continuation of a Task. In short; the compiler knows to look for all await calls and put the rest of the method in a continuation.
The await keyword tells the compiler to call the Task.GetAwaiter() method on the Task ( and basically subscribe to the INotifyCompletion and ICriticalNotifyCompletion) to signal the continuation in the method.
And this I wanted to add just incase you weren't aware. If you do have more than one task that you want to await but would rather await one task as if they were all one then you can do that with Task.WhenAll() So instead of:
var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await taskA;
await taskB;
await taskC;
You could write it a little cleaner like so:
var taskA = DoTaskAsync();
var taskB = DoTaskAsync();
var taskC = DoTaskAsync();
await Task.WhenAll(taskA, taskB, taskC);
And there are more ways of doing this sort of thing built in; just explore it.

How to wait the result of async operations without await?

void A()
{
foreach (var document in documents)
{
var res = records.BulkWriteAsync(operationList, writeOptions); // res is Task<BulkWriteResult<JobInfoRecord>>
}
}
After foreach I would like to wait the result of all BulkWriteAsync, how to do this? I don't want to mark A() as async and do the following
await records.BulkWriteAsync(operationList, writeOptions);
Is it good solution?
void A()
{
var tasks = new List<Task<BulkWriteResult<JobInfoRecord>>>();
foreach (var document in documents)
{
var task = records.BulkWriteAsync(operationList, writeOptions);
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
I call A() in try catch if I will mark public async void A() as async I never be in catch
Well, first you want a Task that represents all the operations. The simplest way to do this is with a bit of LINQ:
Task.WhenAll(documents.Select(i => records.BulkWriteAsync(...)));
Then, you ideally want to await that task. If that isn't possible, you can try
task.GetAwaiter().GetResult();
However, make sure that none of the tasks have thread affinity - that's a great way to get a deadlock. Waiting for a task on the UI thread while the task itself needs the UI thread is a typical example.
The whole point of await is that it allows you to handle asynchronous code as if it were synchronous. So from the outside, it appears as if you never left the method until you actually get to a return (or the end of the method). For this to work, however, your method must return a Task (or Task<T>), and the callee must await your method in turn.
So a code like this:
try
{
tasks = Task.WhenAll(documents.Select(i => ...));
await tasks;
}
catch (Exception ex)
{
// Handle the exception
}
will appear to run completely synchronously, and all exceptions will be handled as usual (though since we're using Task.WhenAll, some will be wrapped in AggregateException).
However, this isn't actually possible to handle with the way .NET and C# is built, so the C# compiler cheats - await is basically a return that gives you a promise of the result you'll get in the future. And when that happens, the control returns back to where the await left the last time. Task is that promise - if you use async void, there's no way for the callee to know what's happening, and it has no option but to continue as if the asynchronous method was a run-and-forget method. If you use async Task, you can await the async method and everything "feels" synchronous again. Don't break the chain, and the illusion is perfect :)

Adding List of tasks without executing

I have a method which returns a task, which I want to call multiple times and wait for any 1 of them to be successful. The issue I am facing is as soon as I add the task to the List, it executes and since I added delay to simulate the work, it just block there.
Is there a way to add the the tasks to the list without really executing it and let whenAny execute the tasks.
The below code is from Linqpad editor.
async void Main()
{
var t = new Test();
List<Task<int>> tasks = new List<Task<int>>();
for( int i =0; i < 5; i++)
{
tasks.Add(t.Getdata());
}
var result = await Task.WhenAny(tasks);
result.Dump();
}
public class Test
{
public Task<int> Getdata()
{
"In Getdata method".Dump();
Task.Delay(90000).Wait();
return Task.FromResult(10);
}
}
Update :: Below one makes it clear, I was under the impression that if GetData makes a call to network it will get blocked during the time it actually completes.
async void Main()
{
OverNetwork t = new OverNetwork();
List<Task<string>> websitesContentTask = new List<Task<string>>();
websitesContentTask.Add(t.GetData("http://www.linqpad.net"));
websitesContentTask.Add(t.GetData("http://csharpindepth.com"));
websitesContentTask.Add(t.GetData("http://www.albahari.com/nutshell/"));
Task<string> completedTask = await Task.WhenAny(websitesContentTask);
string output = await completedTask;
Console.WriteLine(output);
}
public class OverNetwork
{
private HttpClient client = new HttpClient();
public Task<string> GetData(string uri)
{
return client.GetStringAsync(uri);
}
}
since I added delay to simulate the work, it just block there
Actually, your problem is that your code is calling Wait, which blocks the current thread until the delay is completed.
To properly use Task.Delay, you should use await:
public async Task<int> Getdata()
{
"In Getdata method".Dump();
await Task.Delay(90000);
return 10;
}
Is there a way to add the the tasks to the list without really executing it and let whenAny execute the tasks.
No. WhenAny never executes tasks. Ever.
It's possible to build a list of asynchronous delegates, i.e., a List<Func<Task>> and execute them later, but I don't think that's what you're really looking for.
There are multiple tasks in your Getdata method. First does delay, but you are returning finished task which returned 10. Try to change your code like this
return Task.Delay(90000).ContinueWith(t => 10)

Async / await on windows form application

I'm trying to learn and implement async / await keywords on my application. I'm using an API to get data then showing them on my forms. When I try to call methods from an console application there is no problem. But if I call my async methods from Form_Shown event also there no exception but methods not working.
So I'm calling my RefreshOrLoadDataToCache() method on Form_Shown event.
private async void LogTimeReport_Shown(object sender, EventArgs e)
{
// Some syncronous operations
RefreshOrLoadDataToCache(); // Async methods in it
// Some syncronous operations
}
In my this method created a task and wait for it.
private async void RefreshOrLoadDataToCache()
{
if (IsNeededToCallAPI())
{
var taskForTimeEntries = LoadTimeEntriesTemp();
Task.WhenAll(taskForTimeEntries);
DataTable dtTimeEntriesTemp = taskForTimeEntries.Result;
DataTable dtEventsTemp = LoadEventsTemp();
dtTimeEntriesTemp.Merge(dtEventsTemp);
}
else
BindGridViews();
}
This my async method.
private async Task<DataTable> LoadTimeEntriesTemp()
{
TimeEntryHandler timeHandler = new TimeEntryHandler();
TimeEntryResponse response = await timeHandler.GetTimeEntries();
DataTable dt = DatatableHelper.ToDataTable<TimeEntry>(response.TimeEntries);
foreach (DataRow drow in dt.Rows)
{
// Some operations on DataTable
}
return dt;
}
In this method I'm connecting to API and getting results. I think my problem is about this method. Because when I call this method from console application it returns data. But from form application it waits for a long time but there is no result or exception.
private async Task<TimeEntryResponse> GetTimeEntries()
{
using (var client = new AuthorizedHttpClient(_client))
{
var data = await client.GetAsync<TimeEntryResponse>(parameters);
if (data.StatusCode == HttpStatusCode.OK)
{
var response = (TimeEntryResponse)data.ContentObj;
response.Pages = int.Parse(data.Headers.GetValues("X-Pages").First());
response.Page = int.Parse(data.Headers.GetValues("X-Page").First());
response.TotalRecords = int.Parse(data.Headers.GetValues("X-Records").First());
return response;
}
return new TimeEntryResponse() { TimeEntries = null, STATUS = "ERROR" };
}
}
I thought that there is something I'm missing about asyncronous calls on windows forms. How can I fix my code ?
You have a couple of problems with your code
You mark a method as async, but you don't await on the operation inside. You currently do this because RefreshOrLoad is async void. It actually needs to be async Task, where the underlying returned task is the ongoing async operation. Then, the returned Task should be awaited on:
private async void LogTimeReport_Shown(object sender, EventArgs e)
{
// Some syncronous operations
await RefreshOrLoadDataToCache(); // Async methods in it
// Some syncronous operations
}
RefreshOrLoad is an async method. You use Task.WhenAll, which is used for asynchronously waiting on multiple tasks, but you don't await on it either. Then, you call .Result, which causes your code to effectively deadlock. All that's needed is to await the task returning from LoadTimeEntriesTemp:
private async Task RefreshOrLoadDataToCache()
{
if (IsNeededToCallAPI())
{
DataTable dtTimeEntriesTemp = await LoadTimeEntriesTemp();
DataTable dtEventsTemp = LoadEventsTemp();
dtTimeEntriesTemp.Merge(dtEventsTemp);
}
else
BindGridViews();
}
I'd also note that you should use the *Async postfix with your async methods.
When fixing these, you'll see that your code behaves as expected, being asynchronous all the way down.
You problem here:
var taskForTimeEntries = LoadTimeEntriesTemp();
Task.WhenAll(taskForTimeEntries);
DataTable dtTimeEntriesTemp = taskForTimeEntries.Result;
At first, why do you use Task.WhenAll when you have just one task? That way you leak the task returned by Task.WhenAll which will be completed and indicate that all your tasks that are passed to Task.WhenAll are completed. It will wait for the task synchronously which will cause deadlock.
There is a rule for async/await which states await in all ways
So right approach is:
DataTable dtTimeEntriesTemp = await LoadTimeEntriesTemp();
Also, you should await on RefreshOrLoadDataToCache(); in your event handler if you want to do synchronous operations related to its result.
Here is a great article by Stephen Cleary Don't Block on Async Code which describes your problem in more details.
Method RefreshOrLoadDataToCache() is marked as async yet it does not use await on Task.WhenAll() and LogTimeReport_Shown() does not have to be async. :
private async void RefreshOrLoadDataToCache()
{
if (IsNeededToCallAPI())
{
var taskForTimeEntries = LoadTimeEntriesTemp();
DataTable dtTimeEntriesTemp = await taskForTimeEntries; // call await here
DataTable dtEventsTemp = LoadEventsTemp();
dtTimeEntriesTemp.Merge(dtEventsTemp);
}
else
BindGridViews();
}

Categories

Resources