How to call only few method asynchronously in C#? - c#

I know this question has been asked for many times on stack overflow but I am looking for some suggestion on my below code. In my application there are many synchronous methods which are hard to modify. I cannot change everything to async await. But I want to run few methods asynchronously.
I have written some code for that. Also I have added the comments that will help to understand my requirement.
Here is my code:
//This class will perform some heavy operation and also going to call an API for tax configuration.
//The original class takes almost 2 sec to respond. Obviously we are refactoring it but also want this class methods to run async way
public static class TaxCalculatorHelper
{
public static Task<double> CalculateTaxAsync(double salary)
{
// I will do some heavy tax calculation here, so I want it to run asynchronously
return Task.FromResult(500.00); // currently returning temporary value
}
}
//The exisiting classes
public class Employee
{
//This method is not going to be async but What I want that Tax calculation which is heavy task that should run asynchronously
public double GetEmployeeFinalSalary(double salary)
{
var taxValue = Task.Run(async () => await TaxCalculatorHelper.CalculateTaxAsync(salary));
//I was doing this
// return taxValue.Result; // I cannot use this because it blocks the calling thread until the asynchronous operation is complete
//Is the below approach correct ?
return taxValue.GetAwaiter().GetResult();
}
}
public class SomeOtherClass
{
private readonly Employee _employee;
public SomeOtherClass()
{
_employee = new Employee();
}
//This will not be async
public void GetEmployeeCtc(double salary)
{
var finalCtc = _employee.GetEmployeeFinalSalary(salary);
}
}
Can anybody review and suggest me the best approach ?
Thank you !!

Assuming this is a UI application, then using Task.Run to push synchronous work off the UI thread is an acceptable approach. It's not a good idea in ASP.NET apps.
For your specific situation, you need to decide what to do about GetEmployeeFinalSalary.
//This method is not going to be async but What I want that Tax calculation which is heavy task that should run asynchronously
...
// return taxValue.Result; // I cannot use this because it blocks the calling thread until the asynchronous operation is complete
You need to decide whether GetEmployeeFinalSalary will be synchronous or asynchronous. If it's synchronous, then it will block the calling thread - that's what synchronous means.
I suspect that you do not want to block the calling thread (I'm assuming that's the UI thread). In that case, GetEmployeeFinalSalary must be asynchronous in order to free up the calling thread:
public async Task<double> GetEmployeeFinalSalaryAsync(double salary)
{
return await Task.Run(async () => await TaxCalculatorHelper.CalculateTaxAsync(salary));
}

Related

SemaphoreSlim - is it ok to use it in multiple methods to wait and release?

I have an Asp.Net Core 6 Web Api.
I have a Singleton class with several methods in it.
I want only 1 thread to enter any of the methods of the class at a time.
Is it ok to initialize the SemaphoreSlim class in the constructor and use it in every method in the following way? Are there any dangers from it?
Is there a better way to achieve what I am looking for?
public class Foo
{
private readonly SemaphoreSlim _sm;
public Foo()
{
_sm = new SemaphoreSlim(1, 1);
}
public async Task FirstMethod()
{
await _sm.WaitAsync();
//Do some work
_sm.Release();
}
public async Task SecondMethod()
{
await _sm.WaitAsync();
//Do some work
_sm.Release();
}
}
You should adopt the try/catch/finalize pattern - if you do so, you're mostly safe: all code under the WaitAsync will be executed as defined by the max semaphore. Only if the code under the semaphore block, you'll get a deadlock - you could consider using a timeout, but typically this is accepted.
public async Task SecondMethod()
{
await _sm.WaitAsync();
try
{
//Do some work
}
finally
{
//release in case of errors
_sm.Release();
}
}
Other stuff to consider is, especially if it is a long running process, is to use a cancellation token to indicate application closure.
Also keep in mind that if you fire a lot of these methods from various threads, the order is not guaranteed - but they will all be handled.

Is there any way to know if a method is running being awaited from within the method?

I'm a university student but, since I like programming, I try to create a library of code that's been useful to me (something like a code base).
In the process of doing this, I started designing/writing an asynchronous method that's about to be used for interlocking a variable. My goal is to produce different result when this method is being awaited (runs synchronously) and when it isn't.
An example could be the following:
private int _lock;
public async Task<bool> Lock()
{
if (method_is_not_being_awaited)
return Interlocked.Exchange(ref _lock, 1) == 0;
while (0 != Interlocked.Exchange(ref _lock, 1)) {}
return true;
}
Is there any way to achieve such result? If yes, how?
ps: I know that I could make 2 different methods bool lock() and async Task<bool> lockAsync() but, that's not what I ask for
No, it is not possible to do what you want because method must return value before any operation on result (including await) can be performed. It is not specific to async methods but rather how all code behaves in C# (and pretty much any other language).
On other hand it is pretty easy to do something that very close to what you ask - synchronously return value as soon as one tries to await the result of the method: await is essentially just call to GetAwaiter on the result and you can wire it up to alter state of your method.
Note that you can't really know what to do if method ever awaited anyway - so while you can act at moment when await is called you really can't know in advance if you should start asynchronous processing. So the best you can achieve is to do nothing in synchronous part, start asynchronous processing anyway and instantly return result when await is called (aborting/ignoring asynchronous part of the method).
Details on implementing class that can be used as result can be found in https://www.codeproject.com/Articles/5274659/How-to-Use-the-Csharp-Await-Keyword-On-Anything and https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/task-asynchronous-programming-model.
Skeleton code below shows how to implement method that does nothing unless await is called:
class MyTask
{
public MyAwaitable GetAwaiter()
{
return new MyAwaitable();
}
}
class MyAwaitable : INotifyCompletion
{
public bool IsCompleted
{
get { return true; }
}
public int GetResult()
{
return 42; // this is our "result" from method.
}
public void OnCompleted (Action continuation)
{
// just run instantly - no need to save callback as this one is
// always "completed"
continuation();
}
}
MyTask F()
{
// if your really want you can start async operation here
// and somehow wire up MyAwaitable.IsComplete to terminate/abandon
// asynchronous part.
return new MyTask();
}

Prevent Task from starting

Class TaskHolder has a property of type Task. I pass Task as a parameter into constructor and it starts immediately. Is there a way to prevent it from start?
public class Worker
{
public class TaskHolder
{
public TaskHolder(Task objective)
{
Objective = objective;
}
public Task Objective { get; set; }
}
public async Task DoSomething()
{
await Task.Delay(5000);
Debugger.Break(); // Task starts, so debugger stops here!
// Is there a way to prevent it from start?
}
[Test]
public async Task TempTest()
{
// programm starts here:
var t1 = new TaskHolder(DoSomething());
await Task.Delay(10000);
}
}
A Task represents the now-or-future result of an awaitable operation. If that task has already started, that's interesting - but it has nothing to do with the code that is receiving the task.
If you don't want to start something yet, you should probably be passing an Action. You can create a Task from an Action via Task.Run. Or you can simply invoke the action via action() or action.Invoke()`.
If what you want to run is explicitly asynchronous: you can pass Func<Task>. Again, you can start such a delegate with Task.Run if you want a Task that represents the final state. Or you can just invoke it.
(whether to Invoke() it or pass it to Task.Run() depends on whether you want it to use your thread for any of it)
Ultimately, when you called DoSomething(), you started the code running. A timer was scheduled by the Task.Delay, and then the code continued into TaskHolder.

Converting synchrous to asynchrous method in C#

I have gone through several post for converting existing synchronous method to asynchronous. So based on what I have read I am converting the synchronous method to asynchronous like below
Synchronous
public class SomeClass
{
public int DoWork()
{
return DoLongRunningWork();
}
public int DoLongRunningWork()
{
Thread.Sleep(1000);
return 1;
}
}
I converted this to Asynchronous version like below
public class SomeClass
{
public async Task<int> DoWorkAsync()
{
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
return await DoLongRunningWorkAsync();
}
public Task<int> DoLongRunningWorkAsync()
{
Thread.Sleep(1000);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
return Task.Run(() => 1);
}
}
I am calling this from Main() Method
static void Main()
{
Someclass worker = new SomeClass();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
var result = worker.DoWorkAsync().GetAwaiter().GetResult();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
Console.WriteLine(result);
Console.ReadKey();
}
Is this a correct way to convert synchronous to asynchronous method?
I was expecting Main method's ManagedThreadId will be different than DoWorkAsync and DoLongRunningWorkAsync method's ManagedThreadId. But they are same, why?
I used Thread.Sleep() just to simulate the long running method. As per the suggestions below i should have used Task.Delay() to avoid any confusion.
I dont think i have to put my actual business logic here to understand the concept of async.
As per Stephen Cleary
1> Identify the naturally-asynchronous operations your code is doing.
2>Change the lowest-level API calls to invoke asynchronous APIs (with await) instead of synchronous APIs.
However none of the .Net Libarary methods im using inside LongRunningMethod are naturally-asynchronous or awaitable and also LongRunningMethod is not doing any I/O operation.
Requirement
I have Web API which takes JSON string as input. The JSON needs to be transformed into some C# object. For transformation I am building a C# library which takes JSON string as input and then Transform that JSON string into C# objects based on some rules. The library may take time ( few milliseconds) for Transformation, during this I DO NOT want Web API's main thread to block. The main thread should be free to take any other request while the transformation is going on background thread (some other thread).
public class MyWebApi: ApiController
{
private ILib _myLibrary
public MyWebApi(ILib myLibrary)
{
_myLibrary = myLibrary
}
publi async Task<SomeObject> Transform(string jsonString)
{
// i want to make LongRunningMethod() method awaitable
// or at least it needs to execute on different thread so that
// main thread will be free.
var result = await _myLibrary.LongRunningMethod(jsonString);
return result;
}
}
publi class MyLibrary:ILib
{
// A method that does Transformation
public async Task<SomeObject> LongRunningMethod(string jsonString)
{
var result = new SomeObject();
// parse jsonString here
// and based on some complex rules create SomeObject
// this operation may takes time (lets say few milliseconds)
// i may call some other private methods here or public methods from other library
return result
}
}
based on what I have read I am converting the synchronous method to asynchronous like below
The best way to convert synchronous methods to asynchronous (as I describe in my async brownfield article) is the following:
Identify the naturally-asynchronous operations your code is doing. This is anything that is not running CPU code, e.g., I/O.
Change the lowest-level API calls to invoke asynchronous APIs (with await) instead of synchronous APIs.
Note the compiler warning and change your calling method to be async with a proper (Task/Task<T>) return type. Also add an Async suffix.
Change calling methods to use await, and repeat step (3).
When you run into problems with step (3), check out my articles on async OOP, async MVVM (if applicable), and async brownfield.
Applying these to your code:
Identify the naturally-asynchronous operations your code is doing. This is anything that is not running CPU code, e.g., I/O.
Your lowest-level method has a call to Thread.Sleep, which is not running code. The asynchronous equivalent is Task.Delay.
Change the lowest-level API calls to invoke asynchronous APIs (with await) instead of synchronous APIs.
public int DoLongRunningWork()
{
await Task.Delay(1000);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
return 1;
}
Note the compiler warning and change your calling method to be async with a proper (Task/Task<T>) return type. Also add an Async suffix.
public async Task<int> DoLongRunningWorkAsync()
{
await Task.Delay(1000);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
return 1;
}
Change calling methods to use await, and repeat step (3).
public int DoWork()
{
return await DoLongRunningWorkAsync();
}
Note the compiler warning and change your calling method to be async with a proper (Task/Task<T>) return type. Also add an Async suffix.
public async Task<int> DoWorkAsync()
{
return await DoLongRunningWorkAsync();
}
Change calling methods to use await, and repeat step (3).
In this case, since Main cannot be async, you'll need to block with GetAwaiter().GetResult(), just like you currently have.
Final result:
public async Task<int> DoWorkAsync()
{
return await DoLongRunningWorkAsync();
}
public async Task<int> DoLongRunningWorkAsync()
{
await Task.Delay(1000);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
return 1;
}
I was expecting Main method's ManagedThreadId will be different than DoWorkAsync and DoLongRunningWorkAsync method's ManagedThreadId. But they are same, why?
"Asynchronous" does NOT mean "runs on a different thread." See my async intro for more details about how async/await works.

Awaiting a method that is deeply nested on stack

Lets say at some point at least 10 methods are available at stack as not finished. Many of these methods are dealing with actions that make impact on UI. At this point, I would like to issue a Save command. Save command can finish successfully, or can fail. Based on the result, I would like to make different actions, and only then return execution to those methods that are left on stack.
Now, if I run Save command synchronously, there is no problem. I would like to execute Save command asynchronously, return the execution to message pump (UI), while all the code (methods) on stack should wait for SaveCommand to finish.
Now, as I have understood await, there is no guarantee that a call will be made on same thread (in my case UI thread). SO, I cannot just await the first method that was called (the parent of all other methods in stack), since if a different thread gets started, it will raise a UI exception (accessing UI elements from different thread).
So, how to handle this situation? Example code:
public bool PropertyName {get; set { MethodA(); // some code after };}
public void MethodB() { MethodC(); // some code after }
public void MethodC() { MethodD(); // some code after }
public void MethodD() { MethodE(); // some code after }
// etc
void MEthodK()
{
Save();
}
If you want to (asynchronously) wait for a method, just await the Task returned from that method:
public async Task MethodCAsync() { await MethodDAsync(); // some code after }
public async Task MethodDAsync() { await MethodEAsync(); // some code after }
async Task MethodKAsync()
{
await Save();
}
This will cause a problem with your property setter, which now must be an asynchronous method:
public bool PropertyName { get; private set; }
public async Task SetPropertyNameAsync() { await MethodAAsync(); // some code after }
Unless you call ConfigureAwait(), awaiting a Task from a UI thread will always resume running your code on the UI thread.
You don't have to worry about it.

Categories