Issue # 1 - async method is called in a void method
InCorrect Usage:
public static async Task FooAsync()
{
// some async code here...
await Task.Delay(10000);
}
public void ThisWillNotWaitForAsyncCodeToComplete()
{
try
{
Console.WriteLine("Before : " + DateTime.Now.ToString());
FooAsync();
Console.WriteLine("After : " + DateTime.Now.ToString());
}
catch (Exception ex)
{
//The below line will never be reached
Console.WriteLine(ex.Message);
}
}
Correct Usage:
public static async Task FooAsync()
{
await Task.Delay(10000);
}
public async Task ThisWillNotWaitForAsyncCodeToCompleteAsync()
{
Console.WriteLine("Before : " + DateTime.Now.ToString());
await FooAsync();
Console.WriteLine("After : " + DateTime.Now.ToString());
}
Changes to be noted carefully:
- FooAsync() is called using await.
- Method having async Task in the signature
Result:
- Async / await chain is followed properly.
- Parent thread will wait for the child thread to complete. In case of any database or event operations being performed, parent thread will wait for the completion before quitting.
- SEVERITY Level = FATAL
Issue # 2 - no “async” keyword used and “task” object is returned
InCorrect Usage:
public static Task BarAsync()
{
// some async code here...
return Task.Delay(10000);
}
public void ThisWillNotWaitForAsyncCodeToComplete()
{
try
{
Console.WriteLine("Before : " + DateTime.Now.ToString());
BarAsync();
Console.WriteLine("After : " + DateTime.Now.ToString());
}
catch (Exception ex)
{
//The below line will never be reached
Console.WriteLine(ex.Message);
}
}
Correct Usage:
public static async Task BarAsync()
{
await Task.Delay(10000);
}
public async Task ThisWillNotWaitForAsyncCodeToCompleteAsyncAsync()
{
Console.WriteLine("Before : " + DateTime.Now.ToString());
await BarAsync();
Console.WriteLine("After : " + DateTime.Now.ToString());
}
Changes to be noted carefully:
- FooAsync() is using async and not returning task.
- ThisWillNotWaitForAsyncCodeToCompleteAsync() is calling FooAsync() using await.
Result:
- Async / await chain is followed properly.
- Parent thread will wait for the child thread to complete. In case of any database or event operations being performed, the parent thread will wait for the completion before quitting.
- SEVERITY Level = FATAL
Issue # 3 - Method is marked as “async Task” but no async method is called inside.
InCorrect Usage:
Public void Foo()
{}
Public void Bar()
{}
public async Task FakeAsyncMethod()
{
Foo();
Bar();
Return Task.CompletedTask;
}
Correct Usage:
Public void Foo()
{}
Public void Bar()
{}
public void FakeAsyncMethod()
{
Foo();
Bar();
}
Changes to be noted carefully:
- Remove async task from the FakeAsyncMethod()
- Removed return task statement.
Result:
- Normally the sync method should be called as expected.
- SEVERITY level = Important
Issue # 4 - async has blocking call instead of using await
InCorrect Usage :
public static async Task FooAsync()
{
await Task.Delay(10000);
}
public void ThisWillNotWaitForAsyncCodeToCompleteAsync()
{
Console.WriteLine("Before : " + DateTime.Now.ToString());
FooAsync().Result;
Console.WriteLine("After : " + DateTime.Now.ToString());
}
Correct Usage :
public static async Task FooAsync()
{
await Task.Delay(10000);
}
public void ThisWillNotWaitForAsyncCodeToCompleteAsync()
{
Console.WriteLine("Before : " + DateTime.Now.ToString());
await FooAsync();
Console.WriteLine("After : " + DateTime.Now.ToString());
}
Changes to be noted carefully:
- added await in FooAsync() to follow the appropriate async/await chain.
- Using .Result deprives off the async benefit
Result:
- async method should be called as per the recommendation and the correct way of doing.
- SEVERITY level = Important
Issue # 5 - Blocking async method with .Wait
InCorrect Usage :
public async Task FooAsync(string id)
{
… some more function code without any await operation inside…
await Task.Delay(10000);
}
public void Bar()
{
console.writeline(“Hello world”);
FooAsync().Wait();
}
Correct Usage :
public async Task FooAsync(string id)
{
… some more function code without any await operation inside…
await Task.Delay(10000);
}
public async Task BarAsync()
{
console.writeline(“Hello world”);
await FooAsync();
}
Changes to be noted carefully:
- Use async await in BarAsync() to follow the appropriate async/await pattern.
Result:
- async method should be called as per the way they are supposed to rather than forcefully converting them to sync and blocking the current thread.
- SEVERITY level = Important
Issue # 6 - Create task for sync method and waiting on the task.
InCorrect Usage :
public void SomeMethod1()
{
… some function code ….
Var task = Task.Run(()=>SomeMethod2);
task.Wait();
… some functional code….
}
public void SomeMethod2()
{
… Some function code goes here ...
}
Correct Usage :
public void SomeMethod1()
{
… some function code ….
SomeMethod2();
… some functional code….
}
public void SomeMethod2()
{
… Some function code goes here ...
}
Changes to be noted carefully:
- Use sync method normally the way it is supposed to use. Making a task and then waiting on the task is just wasting an additional thread on the pool when the same work can be done the main thread itself.
Result:
- Optimized performance rather than bloating CPU for creation of task when method has to be waited anyway.
- SEVERITY level = Important
Issue # 7 - Retrieving result of multiple tasks
InCorrect Usage :
public async Task<string> FooAsync()
{
string result = string.empty;
… some function code…
return result;
}
public async Task<string> BarAsync()
{
string result = string.empty;
… some function code…
return result;
}
public void ParentMethod()
{
var task1 = FooAsync();
var task2 = BarAsync();
Task.WaitAll(task1,task2);
}
Correct Usage :
public async Task<string> FooAsync()
{
string result = string.empty;
… some function code…
return result;
}
public async Task<string> BarAsync()
{
string result = string.empty;
… some function code…
return result;
}
public async Task ParentMethod()
{
var task1 = FooAsync();
var task2 = BarAsync();
await task.WhenAll(task1,task2);
}
Changes to be noted carefully:
- We should avoid mixing blocking & unblocking code. Task.WaitAll is blocking call whereas Task.WhenAll is nonblocking and maintains the async semantics.
Result:
- Code is optimized and works as per the async / await programming guidelines of avoiding blocking calls.
- SEVERITY level = Important
Issue # 8 - Sync version used when Async is available.
InCorrect Usage :
public bool CheckLabelAlreadyExist(string labelName, Guid facilityKey, int labelTypeCode)
{
return GetQueryable().Any(x => x.DescriptionText == labelName && x.FacilityKey == facilityKey && x.LabelTypeCode == labelTypeCode);
}
CorrectUsage:
public async Task<bool> CheckLabelAlreadyExist(string labelName, Guid facilityKey, int labelTypeCode)
{
return await GetQueryable().AnyAsync(x => x.DescriptionText == labelName && x.FacilityKey == facilityKey && x.LabelTypeCode == labelTypeCode);
}
Task Waiting General Rules (phase 1):
Method Naming Conventions:
- All async methods should have “async” suffix in the method name for easy readability and differentiation between sync and async methods.
- Having “async” in the methods, make it more prominent and reduces the chances of error in implementation.
- This can be done in phase 2
References :
This brought under the Guidence of Vishal Sharma (Solution Architech)