Reputation: 2869
Imagine the following situation. There is an UI and s long running operation must be called without blocking the Main thread. the long running operation calls intself some other methods, which don't interact with the UI thread.
In Most cases, the methods which are called from Method B and C have synchronous alternatives to their Async counterparts. The question is: can they be used safely instead of their async counterpart? Lets say DbContext.DbSet.Add instead of AddAsync
// UI
public async Task UiMethodAsync()
{
var result = await MethodAAsync();
}
// some component
public async Task<bool> MethodAAsync()
{
return await MethodBAsync().ConfigureAwait(false);
}
public async Task<bool> MethodBAsync()
{
return await MethodCAsync().ConfigureAwait(false);
}
public async Task<bool> MethodCAsync()
{
return await DbContext.Set<TEntit>.AnyAsync().ConfigureAwait(false);
}
My question is: Is it neccessary to make all the methods asynchronous to prevent UI thread from blocking or would it be eben good enough to make Method B and C synchronously like this:
// UI
public async Task UiMethodAsync()
{
var result = await MethodAAsync();
}
// some component
public Task<bool> MethodAAsync()
{
return Task.FromResult(MethodB());
}
public bool MethodB()
{
return MethodC();
}
public bool MethodC()
{
return DbContext.Set<TEntit>.Any();
}
of course this depends on what MethodB and C are doing, if they interact with the ui thread or not. but let's say they don't and just have to calculate things and return a result.
Is there a need to make them asynchronous as well? I guess no. I think it probably avoids unnecessary overhead for managing tasks and threads.
Upvotes: 1
Views: 136
Reputation: 456322
Generally speaking, if you want to make something asynchronous, you start at the lowest level - the APIs that are actually doing I/O work. In your example, AnyAsync
would be the first thing made asynchronous. Then allow the asynchrony to grow from there (up through MethodC
, then MethodB
, then MethodA
, and finally UiMethod
).
It is normal to end up with asynchrony going "all the way".
Is there a need to make them asynchronous as well? I guess no.
Yes. If you want them to be asynchronous, then they need to be asynchronous. Task.FromResult
is for synchronous implementations; they won't be asynchronous.
Imagine the following situation. There is an UI... the methods which are called from Method B and C have synchronous alternatives to their Async counterparts. The question is: can they be used safely instead of their async counterpart?
One thing you can get away with in UI world is by wrapping calls to synchronous methods in Task.Run
. E.g.:
public async Task UiMethodAsync()
{
var result = await Task.Run(MethodA);
}
This is a useful technique for situations where you have a client-side app, don't want to block the UI, but also don't want to take the time to convert all the code to being asynchronous. Note that this is not appropriate for server-side applications such as ASP.NET.
Upvotes: 1
Reputation: 7545
Just adding the async keyword is not enough to make your code not block.
A function returning a Task will still block unless the calls are async all the way down.
It's difficult to explain, but to highlight one bit of your code:
public async Task<bool> MethodA()
{
return Task.FromResult(MethodB());
}
Is equivalent to
public async Task<bool> MethodA()
{
bool b = MethodB();
return Task.FromResult(b);
}
It is clear now that the first line of code is blocking, and the Task isn't created until the second line.
Upvotes: 2
Reputation: 43379
A method that returns a Task
creates the expectation that will not block the calling thread. At least not for a substantial amount of time. But this is not enforced by the async-await machinery. It is possible, and actually very easy, to write a method that breaks this expectation. For example:
public Task DoStuffTheWrongWayAsync()
{
Thread.Sleep(1000); // Simulate a heavy computation, or a blocking call
return Task.CompletedTask;
}
Any thread that calls this method will be blocked for one second, and then will be handed a completed task. I don't know if this antipattern has an established name. Fake-asynchrony comes in mind, although this can also be used for the case that the caller is not blocked, but another poor thread is blocked instead. The bottom line is that a well behaved asynchronous method should return a Task
immediately, leaving the calling thread free to do other work (like responding to UI events if it's a UI thread).
Upvotes: 2