How to convert synchronous method to asynchronous without changing it's signature - c#

I have a large scale C# solution with 40-ish modules.
I'm trying to convert a service used solution-wide from synchronous to asynchronous.
the problem is I can't find a way to do so without changing the signature of the method.
I've tried wrapping said asynchronous desired operation with Task but that requires changing the method signature.
I've tried changing the caller to block itself while the method is operating but that screwed my system pretty good because it's a very long calling-chain and changing each of the members in the chain to block itself is a serious issue.
public SomeClass Foo()
{
// Synchronous Code
}
Turn this into:
public SomeClass Foo()
{
//Asynchronous code
}
whilst all callers stay the same
public void DifferentModule()
{
var class = Foo();
}

Any implementation that fundamentally changes something from sync to async is going to involve a signature change. Any other approach is simply not going to work well. Fundamentally: async and sync demand different APIs, which means: different signatures. This is unavoidable, and frankly "How to convert synchronous method to asynchronous without changing it's signature?" is an unsolvable problem (and more probably: the wrong question). I'm sorry if it seems like I'm not answering the question there, but... sometimes the answer is "you can't, and anyone who says you can is tempting you down a very bad path".
In the async/Task<T> sense, the most common way to do this without breaking compatibility is to add a new / separate method, so you have
SomeReturnType Foo();
and
Task<SomeReturnType> FooAsync(); // or ValueTask<T> if often actually synchoronous
nothing that Foo and FooAsync here probably have similar but different implementations - one designed to exploit async, one that works purely synchronously. It is not a good idea to spoof one by calling the other - both "sync over async" (the synchronous version calling the async version) and "async over sync" (the async version calling the sync version) are anti-patterns, and should be avoided (the first is much more harmful than the second).
If you really don't want to do this, you could also do things like adding a FooCompleted callback event (or similar), but : this is still fundamentally a signature change, and the caller will still have to use the API differently. By the time you've done that - you might as well have made it easy for the consumer by adding the Task<T> API instead.

The common pattern is to add an Async to the method name and wrap the return type in a Task. So:
public SomeClass Foo()
{
// Synchronous Code
}
becomes:
public Task<SomeClass> FooAsync()
{
// Asynchronous Code
}
You'll end up with two versions of the method, but it will allow you to gradually migrate your code over to the async approach, and it won't break the existing code whilst you're doing the migration.

If you desperately need to do this, it can be achieved by wrapping the Synchronous code that needs to become Asynchronous in a Task this can be done like this:
public SomeClass Foo()
{
Task t = Task.Run(() =>
{
// Do stuff, code in here will run asynchronously
}
t.Wait();
// or if you need a return value: var result = t.Wait();
return someClass;
// or return result
}
Code you write inside the Task.Run(() => ...) will run asynchronously
Short explanation: with Task t = Task.Run(() => ...) we start a new Task, the "weird" parameter is a Lambda expression, basically we're passing a anonymous Method into the Run method which will get executed by the Task
We then wait for the task to finish with t.Wait();. The Wait method can return a value, you can return a value from an anonymous method just like from any method, with the return keyword
Note: This can, but should not be done. See Sean's answer for more

Related

What's the generally accepted best practice when calling NON-Async functions, from within other async functions?

It seems that in a Blazor Server app we are encouraged to use Async where possible, and I generally understand why. That said - would someone please be kind enough to explain the general expectation when using Async functions and fill in some missing knowledge for me - I'm confused by all the information that's out there and need something specific to what I'm doing, to help me understand.
I'm trying to stick to using async where possible and in most cases it's fairly easy to do so, but when calling some functions, it seems overkill to make them asynchronous (or is it?). In the the example below - the NON-async 'ValidateEvent' function is being called from within an async function.
So my question is ...Should I:
a) Call it normally from within the Async function (which seems to defeat the point of async) eg: "var validationResult = ValidateEvent(objDto);"?
b) Call it using Task.Run eg: "await Task.Run(() =>ValidateEvent(objDto));"?
c) Convert this simple IF/ELSE method into an Async function?
Thanks in advance for any help/advice.
//example async function, that itself calls the other non-async function.
public async Task<Response> AddAsync(ObjDto objDto)
{
// a) Call it normally?
var validationResult = ValidateEvent(objDto);
// b) Calling it using Task.Run?
var validationResult = await Task.Run(() =>ValidateEvent(objDto));
//Do stuff asynchronously here
...
await _db.AddAsync(objDto);
await _db.SaveChangesAsync();
...
}
The validation function:
c) Should I really be converting this to async since it's just a series of 'IFs and ELSEs' (async conversion further below)
//Non-Async version
public ResponseObj ValidateEvent(ObjDto obj)
{
ResponseObj responseObj = new();
string stringErrors = "";
//If not full day, check that end date is not before start date
if (!obj.IsFullDay)
{
if (obj.EndTime < obj.StartTime)
stringErrors += ("End date cannot be before the start date<br />");
}
...other code removed for brevity
if (string.IsNullOrEmpty(stringErrors)) //Success
{
responseObj.Status = responseObj.Success;
}
else //errors
{
responseObj.Status = responseObj.Error;
responseObj.Message = stringErrors;
}
return responseObj;
}
//Example Async conversion - is it worth converting this using Task.Run?
//Async conversion
public async Task<ResponseObj> ValidateEvent(ObjDto obj)
{
ResponseObj responseObj = new();
string stringErrors = "";
await Task.Run(() =>
{
//If not full day, check that end date is not before start date
if (!obj.IsFullDay)
{
if (obj.EndTime < obj.StartTime)
stringErrors += ("End date cannot be before the start date<br />");
}
if (string.IsNullOrEmpty(stringErrors)) //Success
{
responseObj.Status = responseObj.Success;
}
else //errors
{
responseObj.Status = responseObj.Error;
responseObj.Message = stringErrors;
}
}
);
return responseObj;
}
Again, thanks in advance for any help/advice in understaning the best way to go with this in general.
a) Call it normally from within the Async function ...
Yes
... (which seems to defeat the point of async)
No it doesn't.
It depends on what the ValidateEvent method does.
In case it does a trivial amount of work that is not going to be increased over time, like your example suggests, then it's completely OK to invoke it from inside the asynchronous AddAsync method.
In case it blocks for a considerable amount of time, say for more than 50 msec, then invoking it from inside an asynchronous method would violates Microsoft's guidelines about how asynchronous methods are expected to behave:
An asynchronous method that is based on TAP can do a small amount of work synchronously, such as validating arguments and initiating the asynchronous operation, before it returns the resulting task. Synchronous work should be kept to the minimum so the asynchronous method can return quickly.
So what you should do if the ValidateEvent method blocks, and you can't do anything about it? AFAIK you are in a gray area, with all options being unsatisfactory for one reason or another.
You can leave it as is, accepting that the method violates Microsoft's guidelines, document this behavior, and leave to the callers the option to wrap the AddAsync in a Task.Run, if this is beneficial for them.
You can wrap the ValidateEvent in a Task.Run, like you did in your question. This violates (partially) another guideline by Microsoft: Do not expose asynchronous wrappers for synchronous methods.
You can split your AddAsync method into two parts, the synchronous ValidateAdd and the asynchronous AddAsync, expecting that the callers will invoke the one method after the other.
If your AddAsync method is part of an application-agnostic library, I would say go with the option 1. That's what Microsoft is doing with some built-in APIs. If the AddAsync method is application-specific, and the application has a GUI (WinForms, WPF etc), then it is tempting to go with the option 2. Keeping the UI responsive is paramount for a GUI application, so the Task.Run should be included somewhere, and putting it inside the AddAsync seems like a convenient place to put it. The option 3 is probably the least attractive option for this particular case, because validating and adding are tightly coupled operations, but it may be a good option for other scenarios.

C# Async when also needing support for synchronous call

I'm in a situation where we have some code that is run by user input (button click), that runs through a series of function calls and result in generating some data (which is a quite heavy operation, several minutes). We'd like to use Async for this so that it doesn't lock up the UI while we're doing this operation.
But at the same time we also have a requirement that the functions will also be available through an API which preferably should be synchronous.
Visualization/Example (pseudo-code):
public async void Button_Click() // from UI context
{
await instanceOfClassA.FuncA();
// some code
}
public async Task ClassA.FuncA()
{
await instanceOfClassB.FuncB()
// some code
}
public async Task ClassB.FuncB()
{
await instanceOfClassC.SomeHeavyFunc()
// some code
}
public async Task ClassC.SomeHeavyFunc()
{
// some heavy calculations
}
// Also need to provide a public synchronous API function
public void SomeClass.SynchronousAPIFunc()
{
// need to call differentInstanceOfClassB.FuncB()
}
Is there a way to make it so that the public API function does the waiting for the async operation internally?
EDIT:
In this post, user Rachel provides two answers to the question. Both seem interesting, though I'm unsure which one would offer the least amount of risk/side effects.
EDIT2:
I should note that we're using .NET v4.6.1.
Thanks in advance.
The problem with making "synchronous" versions of your methods that just call the asynchronous versions is that it can cause deadlocks, especially if the person calling this code is not aware that this is what is happening.
If you really want to make synchronous versions, then follow Microsoft's lead and write completely new methods that do not use any asynchronous code. For example, the implementation for File.ReadAllLines() doesn't use any of the same code as File.ReadAllLinesAsync().
If you don't want to do that, then just don't provide synchronous versions of your methods. Let the caller make the decision on how to deal with it. If they want to block synchronously on it, then they can mitigate the risk of deadlock.
But at the same time we also have a requirement that the functions will also be available through an API which preferably should be synchronous.
If you have the need to expose both a synchronous and asynchronous API, I recommend the boolean argument hack. This looks like:
public Task<T> FuncBAsync() => FuncBAsync(sync: false);
public T FuncB() => FuncBAsync(sync: true).GetAwaiter().GetResult();
public async Task<T> FuncBAsync(bool sync)
{
// Note: is `sync` is `true`, this method ***must*** return a completed task.
...
}
Is there a way to make it so that the public API function does the waiting for the async operation internally?
I do not recommend using direct blocking (e.g., GetAwaiter().GetResult()), as the straightforward implementation will lead to deadlocks.
EDIT: In this post, user Rachel provides two answers to the question.
I strongly recommend against using that solution. It uses a nested message loop with a custom SynchronizationContext, but doesn't do COM pumping. This can cause problems particularly if called from a UI thread. Even if the pumping isn't a problem, this solution can cause unexpected re-entrancy, which is a source of countless, extremely subtle, and difficult-to-find bugs.
You can utilize .GetAwaiter().GetResult()
as per your example, it would look like:
public void SomeClass.SynchronousAPIFunc()
{
// need to call differentInstanceOfClassB.FuncB()
ClassB.FuncB().GetAwaiter().GetResult();
}
Also, a good reference on when to not use the above can be found at Dont Block on Async Code

Is having a return type of Task enough to make a method run asynchronously?

I have a simple method that does a complicated string operation and returns the result. As you can see, the return type of this method is Task<string>. Therefore, I can use Task.FromResult(result) to return the value of my string.
public Task<string> ComplexOperation()
{
string result = // Do something complex
return Task.FromResult(result);
}
Then, I can use the await keyword to call this method.
public static async Task Main(string[] args)
{
var myResult = await ComplexOperation();
}
Because I am awaiting CompelxOperation(), (waiting for the Task to return on completion) will this method run asynchronously?
Based on your comments you need to do something like this:
public Task<string> ComplexOperation()
{
return Task.Run(() => /* Do something complex */);
}
You can play with it like this:
public static async Task Main()
{
Console.WriteLine("Before" );
var myResultTask = ComplexOperation();
Console.WriteLine("After task creation");
var result = await myResultTask;
Console.WriteLine("After async await");
}
public Task<string> ComplexOperation()
{
Console.WriteLine("Creation");
return Task.Run(() =>
{
Console.WriteLine("In before work");
Thread.Sleep(500); //simulate work;
Console.WriteLine("In after work");
return "Done";
});
}
And compare the behavior with switching to your implementation when you just return Task.FromResult. Also it does not makes much sense in such test example, TBH.
Just as you flagged as async the Main method to get an asynchronous method you need to flag the method as asynchronous with the async keyword, and make the result a Task.
But in the framework Tasks are used for 2 related but different concepts, parallelization and asynchronous programming.
What you are doing is use parallelization.
So you are just running some synchronous code on a different thread.
Based on your example i think what you need is to use parallelization, which can speed up some complex calculation or work by use many threads to work in parallel.
The concept behind asynchronous code is to free the thread while you are waiting for external resources (like some data from a web service), to allow other work to be done in the meantime.
So, if you need to do complex work with local resources it's better use tasks without async logic, instead when working with remote resources you can go for asynchronous operations.
Semantically the method ComplexOperation is asynchronous because it returns an awaitable type, and in order to follow the guidelines it should be named ComplexOperationAsync.
Asynchronous methods in TAP include the Async suffix after the operation name for methods that return awaitable types, such as Task, Task<TResult>, ValueTask, and ValueTask<TResult>.
But it is not a well behaved asynchronous method. An asynchronous method is expected to return an incomplete Task immediately, allowing the caller to await the task asynchronously without been blocked. From the docs:
An asynchronous method that is based on TAP can do a small amount of work synchronously, such as validating arguments and initiating the asynchronous operation, before it returns the resulting task. Synchronous work should be kept to the minimum so the asynchronous method can return quickly.
The ComplexOperation method does exactly the opposite: it forces the calling thread to perform the complex operation, and finally it hands back a completed task. For all intents and purposes this operation is not asynchronous at all. It is 100% synchronous and 0% asynchronous, plus some overhead (to add insult to injury). So don't do this, and if there is some library that does it, don't use the library. It is simply bad practice.
Clarification: Since the term asynchronous method can mean different things in different contexts, I should clarify that the context of this answer is the perspective of the consumer, who sees the signature of the method as a contract, and builds expectations based on the visible contract and not on the invisible implementation. In this context the method ComplexOperation is asynchronous. If we switch perspective and focus on the implementation, the method ComplexOperation is not asynchronous. My statement «it is simply bad practice» refers to the practice of breaking the contract, and providing a synchronous implementation to a method with asynchronous contract. I am not criticizing the use of Task.FromResult method per se. I am criticizing it only when it follows a complex/lengthy/latent operation, that according to the method's contract should be made asynchronous.
P.S. I am thankful to #DmytroMukalov for providing (in the chat) the helpful distinction between asynchrony by contract and by implementation.

Does adding a Task<T> to the return of a method make it run in a thread?

I am struggling to find any solid information online, but lets pretend I have a method like below:
public int DoSomething()
{
// some sync logic that takes a while
return someResult
}
So lets assume it is a method that is completely sync and currently will block the thread the caller is on for 100ms. So then lets say I wanted to create a version that would not block that thread while it executes, is it as simple as just doing:
public Task<int> DoSomethingAsync()
{
// some sync logic that takes a while
return Task.FromResult(someResult)
}
As I know Task.Run will cause code to be executed within a thread, but does .net basically interpret any method with a Task/<T> return type to be something that should be non blocking?
(I am hesitant to use the word async as it has mixed meanings in .net, but as shown above there is no await use here)
No. All that changing the return type does is... change the return type.
You're changing your promise from "when I return I will give you an int" to "when I return, I will give you something that will eventually provide an int" (with the caveat that the value may, in fact, already be available).
How your method comes up with the Task is entirely an internal implementation detail that is a concern for your method. In your current changed implementation, it does it by running entirely on the same thread from which it was called and then just stuffing the result into a Task at the end - to no real benefit.
If you had added the async keyword and left the return someResult; line you'd have been given more clues about this by the compiler:
This async method lacks 'await' operators and will run synchronously. Consider using ...

How to properly write a custom Task returning method

For running code asynchronously (eg with async/await) I need a proper Task. Of course there are several predefined methods in the framework which cover the most frequent operations, but sometimes I want to write my own ones. I’m new to C# so it’s quite possible that I’m doing it wrong but I’m at least not fully happy with my current practice.
See the following example for what I’m doing:
public async Task<bool> doHeavyWork()
{
var b1 = await this.Foo();
//var b2 = await Task<bool>.Factory.StartNew(Bar); //using Task.Run
var b2 = await Task.Run(()=>Bar());
return b1 & b2;
}
public Task<bool> Foo()
{
return Task.Factory.StartNew(() =>
{
//do a lot of work without awaiting
//any other Task
return false;
});
}
public bool Bar()
{
//do a lot of work without awaiting any other task
return false;
}
In general I create and consume such methods like the Foo example, but there is an ‘extra’ lambda containing the whole method logic which doesn't look very pretty imho. Another option is to consume any Method like the Bar example, however I think that’s even worse because it isn’t clear that this method should be run async (apart from proper method names like BarAsync) and the Task.Factory.StartNew may has to be repeated several times in the program. I don’t know how to just tell the compiler ‘this method returns a Task, please wrap it as a whole into a Task when called’ which is what I want to do.
Finally my question: What’s the best way to write such a method? Can I get rid of the ‘extra’ lambda (without adding an additional named method of course)?
EDIT
As Servy stated there is always good reason to have a synchronous version of the method. An async version should be provided only if absolutley necessary (Stephen Cleary's link).
Would the world end if someone wanted to call Bar without starting it in a new thread/Task? What if there is another method that is already in a background thread so it doesn't need to start a new task to just run that long running code? What if the method ends up being refactored into a class library that is called from both desktop app context as well as an ASP or console context? In those other contexts that method would probably just need to be run directly, not as a Task.
I would say your code should look like Bar does there, unless it's absolutely imperative that, under no circumstances whatsoever, should that code be run without starting a new Task.
Also note that with newer versions of .NET you should be switching from Task.Factory.StartNew to Task.Run, so the amount of code should go down (a tad).

Categories