What happens when we don't await an async method - c#

I have a .NET CORE 2 backend. In one of my controller endpoints, I'm creating invitations to be sent out via email. This seems to be a huge bottleneck on the endpoint and after thinking about it, I don't really need to wait for these invitations. If the email fails to send out, I can't really do anything about it anyway.
If I don't do await sendFn() would it essentially be a fire and forget method? I was reading on another stackoverflow thread that I'd have to do sendFn().ContinueWith(t => throw(t)) to be able to catch the exception since it'll be in another thread.
I have similar mailing functions around the code base. They each do slightly different things, but is there a service fn I can do to wrap these to make them fire and forget? I think some places I can just not use await (if that works), but some things alter the database context so if I don't await them I can potentially run into a case where something is accessing the same db context.
[HttpPost]
public async Task<IActionResult> CreateEvent([FromBody] Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
await SendInvitations(val); // fn in question
return Ok();
}
public async Task SendInvitation(Event event)
{
forEach (var person in event.people)
{
await _ctx.Invitation.Add(person); // This shouldn't happen while another iteration or some other async code elsewhere is using the db ctx.
_ctx.SaveChangesAsync();
await _mailService.SendMail(person.email,"you have been invited"); // don't really need to await this.
}
}
I'm posting to my server with data about an event. After I create and save the event to the database, I go and create invitations for each person. These invitations are also database items. I then send out an email. I'm mostly worried that if I drop the await, then when I'm creating invitations, it may conflict with db context elsewhere or the next iteration.

To get your code to compile and run I had to make these changes:
public async Task<IActionResult> CreateEvent(Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
await SendInvitation(val);
return Ok();
}
public async Task SendInvitation(Event #event)
{
foreach (var person in #event.people)
{
await _ctx.Invitation.Add(person);
await _ctx.SaveChangesAsync();
await _mailService.SendMail(person.email, "you have been invited");
}
}
I also had to write this harness code:
public OK Ok() => new OK();
public class Event
{
public List<Person> people = new List<Person>();
}
public class Person
{
public string email;
}
public interface IActionResult { }
public class OK : IActionResult { }
public class Invitation
{
public Task Add(Person person) => Task.Run(() => { });
}
public static class _ctx
{
public static List<Event> Event = new List<Event>();
public static Invitation Invitation = new Invitation();
public static Task SaveChangesAsync() { return Task.Run(() => { }); }
}
public static class _mailService
{
public static Task SendMail(string email, string message) { return Task.Run(() => { }); }
}
Then I updated SendInvitation like this:
public async Task SendInvitation(Event #event)
{
Thread.Sleep(2000);
foreach (var person in #event.people)
{
await _ctx.Invitation.Add(person);
await _ctx.SaveChangesAsync();
await _mailService.SendMail(person.email, "you have been invited");
}
Console.WriteLine("Done `SendInvitation`.");
}
Now, I can run it all like so:
var e = new Event();
e.people.Add(new Person() { email = "foo#bar.com" });
CreateEvent(e).ContinueWith(t => Console.WriteLine("Done `CreateEvent`."));
Console.WriteLine("Done `Main`.");
That outputs:
Done `Main`.
Then 2 seconds later:
Done `SendInvitation`.
Done `CreateEvent`.
If I simply change CreateEvent to this:
public async Task<IActionResult> CreateEvent(Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
Task.Run(() => SendInvitation(val));
return Ok();
}
Then I get this output:
Done `Main`.
Done `CreateEvent`.
Then 2 seconds later:
Done `SendInvitation`.
That seems to be what you want.

The short answer is that you have no guarantees that that the execution of that code will complete.
That's why ASP.NET Core has infrastructure for background work: Implementing background tasks in .NET Core 2.x webapps or microservices with IHostedService and the BackgroundService class

Related

How can I get the result of my work in a Task

I need to do a work in a Task (infinite loop for monitoring) but how can I get the result of this work?
My logic to do this stuff i wrong? This is a scope problem I think.
There is an example simplified:
The variable is "first" and I want "edit"
namespace my{
public class Program{
public static void Main(string[] args){
Logic p = new Logic();
Task t = new Task(p.process);
t.Start();
Console.WriteLine(p.getVar());// result="first"
}
}
public class Logic{
public string test = "first";
public void process(){
while(true){
//If condition here
this.test = "edit";
}
}
public String getVar(){
return this.test;
}
}
}
It can be done using custom event. In your case it can be something like:
public event Action<string> OnValueChanged;
Then attach to it
p.OnValueChanged += (newValue) => Console.WriteLine(newValue);
And do not forget to fire it
this.test = "edit";
OnValueChanged?.Invoke(this.test);
Tasks aren't threads, they don't need a .Start call to start them. All examples and tutorials show the use of Task.Run or Task.StartNew for a reason - tasks are a promise that a function will execute at some point in the future and produce a result. They will run on threads pulled from a ThreadPool when a Task Scheduler decides they should. Creating cold tasks and calling .Start doesn't guarantee they will start, it simply makes the code a lot more difficult to read.
In the simplest case, polling eg a remote HTTP endpoint could be as simple as :
public static async Task Main()
{
var client=new HttpClient(serverUrl);
while(true)
{
var response=await client.GetAsync(relativeServiceUrl);
if(!response.IsSuccessStatusCode)
{
//That was an error, do something with it
}
await Task.Delay(1000);
}
}
There's no need to start a new Task because GetAsync is asynchronous. WCF and ADO.NET also provide asynchronous execution methods.
If there's no asynchronous method to call, or if we need to perform some heavey work before the async call, we can use Task.Run to start a method in parallel and await for it to finish:
public bool CheckThatService(string serviceUrl)
{
....
}
public static async Task Main()
{
var url="...";
//...
while(true)
{
var ok=Task.Run(()=>CheckThatService(url));
if(!ok)
{
//That was an error, do something with it
}
await Task.Delay(1000);
}
}
What if we want to test multiple systems in parallel? We can start multiple tasks in parallel, await all of them to complete and check their results:
public static async Task Main()
{
var urls=new[]{"...","..."};
//...
while(true)
{
var tasks=urls.Select(url=>Task.Run(()=>CheckThatService(url));
var responses=await Task.WhenAll(tasks);
foreach(var response in responses)
{
///Check the value, due something
}
await Task.Delay(1000);
}
}
Task.WhenAll returns an array with the results in the order the tasks were created. This allows checking the index to find the original URL. A better idea would be to return the result and url together, eg using tuples :
public static (bool ok,string url) CheckThatService(string serviceUrl)
{
....
return (true,url);
}
The code wouldn't change a lot:
var tasks=urls.Select(url=>Task.Run(()=>CheckThatService(url));
var responses=await Task.WhenAll(tasks);
foreach(var response in responses.Where(resp=>!resp.ok))
{
///Check the value, due something
}
What if we wanted to store the results from all the calls? We can't use a List or Queue because they aren't thread safe. We can use a ConcurrentQueue instead:
ConcurrentQueue<string> _results=new ConcurrentQueue<string>();
public static (bool ok,string url) CheckThatService(string serviceUrl)
{
....
_results.Enqueue(someresult);
return (true,url);
}
If we want to report progress regularly we can use IProgress<T> as shown in Enabling Progress and Cancellation in Async APIs.
We could put all the monitoring code in a separate method/class that accepts an IProgress< T> parameter with a progress object that can report success, error messages and the URL that caused them, eg :
class MonitorDTO
{
public string Url{get;set;}
public bool Success{get;set;}
public string Message{get;set;}
public MonitorDTO(string ulr,bool success,string msg)
{
//...
}
}
class MyMonitor
{
string[] _urls=url;
public MyMonitor(string[] urls)
{
_urls=url;
}
public Task Run(IProgress<MonitorDTO> progress)
{
while(true)
{
var ok=Task.Run(()=>CheckThatService(url));
if(!ok)
{
_progress.Report(new MonitorDTO(ok,url,"some message");
}
await Task.Delay(1000);
}
}
}
This class could be used in this way:
public static async Task Maim()
{
var ulrs=new[]{....};
var monitor=new MyMonitor(urls);
var progress=new Progress<MonitorDTO>(pg=>{
Console.WriteLine($"{pg.Success} for {pg.Url}: {pg.Message}");
});
await monitor.Run(progress);
}
Enabling Progress and Cancellation in Async APIs shows how to use the CancellationTokenSource to implement another important part of a monitoring class - cancelling it. The monitoring method could check the status of a cancellation token periodically and stop monitoring when it's raised:
public Task Run(IProgress<MonitorDTO> progress,CancellationToken ct)
{
while(!ct.IsCancellationRequested)
{
//...
}
}
public static async Task Maim()
{
var ulrs=new[]{....};
var monitor=new MyMonitor(urls);
var progress=new Progress<MonitorDTO>(pg=>{
Console.WriteLine($"{pg.Success} for {pg.Url}: {pg.Message}");
});
var cts = new CancellationTokenSource();
//Not awaiting yet!
var monitorTask=monitor.Run(progress,cts.Token);
//Keep running until the first keypress
Console.ReadKey();
//Cancel and wait for the monitoring class to gracefully stop
cts.Cancel();
await monitorTask;
In this case the loop will exit when the CancellationToken is raised. By not awaiting on MyMonitor.Run() we can keep working on the main thread until an event occurs that signals monitoring should stop.
The getVar method is executed before the process method.
Make sure that you wait until your task is finished before you call the getVar method.
Logic p = new Logic();
Task t = new Task(p.process);
t.Start();
t.Wait(); // Add this line!
Console.WriteLine(p.getVar());
If you want to learn more about the Wait method, please check this link.

Async Func in C# - correct async await usage

I want to simplify out repository code and want to use Funcs to provide common functionality.
The problem now is based on this code:
namespace Test
{
public class Demo
{
public Task<Person> GetAsync1(Guid id)
{
return ExecuteAsync(context => context.Persons.GetAsync(id));
}
public async Task<Person> GetAsync2(Guid id)
{
return await ExecuteAsync(async context => await context.Persons.GetAsync(id));
}
public Task<Person> GetAsync3(Guid id)
{
return ExecuteAsync(async context => await context.Persons.GetAsync(id));
}
public Task<TOut> ExecuteAsync(Func<Context, Task<TOut>> func)
{
using(var context = new Context())
{
return func(context);
}
}
}
}
The problem here for me is now how to call the async func correctly.
Which of those three Get methods is correct?
With the first one I think I get a deadlock because it hangs at this point. Number 2 works fine but I think two async/awaits here are not correct, because of the Task re-wrapping!?
Actually all 3 GetAsync implmentations where fine to do (personally I would use GetAsync1), what you have is a bug in ExecuteAsync
public async Task<TOut> ExecuteAsync(Func<Context, Task<TOut>> func)
{
using(var context = new Context())
{
return await func(context);
}
}
By awaiting the output of func you do not dispose of the context until the function has completed it's work.

Callback never comes

I have a WebApi controller which calls a third party API in asynchronous mode.
All works ok and now I want to sort the result in a separate action method.
Now, when I call the API, the callback with the result never happens after running "await client.GetAsycn(...)" in the DAL. What am I missing?
This is my API controller:
// GET api/lookup
[ResponseType(typeof(RestaurantModel))]
public async Task<IHttpActionResult> Get(string outcode)
{
if (string.IsNullOrEmpty(outcode)) throw new ArgumentNullException(nameof(outcode));
var result = await _repository.GetRestaurantsByOutcode(outcode);
return Ok(new RestaurantModel()
{
Result = result
});
}
// GET api/sorted
[System.Web.Http.Route("~/api/sorted")]
public List<Restaurant> GetSorted(string outcode)
{
if (string.IsNullOrEmpty(outcode)) throw new ArgumentNullException(nameof(outcode));
return _repository.GetSortedRestaurantsByOutcode(outcode);
}
This is my repository with a new method to sort the result:
public class RestaurantRepository : IRestaurantRepository
{
private readonly IContext _context;
public RestaurantRepository(IContext context)
{
_context = context;
}
public Task<ApiResult> GetRestaurantsByOutcode(string outcode)
{
return _context.GetRestaurantsByOutcode(outcode);
}
public List<Restaurant> GetSortedRestaurantsByOutcode(string outcode)
{
return _context.GetRestaurantsByOutcode(outcode).Result.Restaurants
.OrderBy(x => x.Name).ToList();
}
}
This is my DAL to call the third party API:
public async Task<ApiResult> GetRestaurantsByOutcode(string outcode)
{
using (var client = new HttpClient())
{
ConfigureHttpClient(client);
var response = await client.GetAsync(
$"restaurants?q={WebUtility.UrlEncode(outcode)}");
return response.IsSuccessStatusCode
? await response.Content.ReadAsAsync<ApiResult>()
: null;
}
}
You have a mix-match of sometimes you use async/await and other times you don't. Async / await (can and does by default) ensures that the call resumes on the calling thread so the context is resulted. You need to allign your code so you make use of the async/await in the whole stack. Otherwise you are creating a deadlock for your self.
[System.Web.Http.Route("~/api/sorted")]
// missing async in signature (not good if you are calling it with await in your controller)
public async Task<List<Restaurant>> GetSorted(string outcode)
{
if (string.IsNullOrEmpty(outcode)) throw new ArgumentNullException(nameof(outcode));
// added await in call
return await _repository.GetSortedRestaurantsByOutcode(outcode);
}
DAL
public class RestaurantRepository : IRestaurantRepository
{
private readonly IContext _context;
public RestaurantRepository(IContext context)
{
_context = context;
}
// added async and await
public async Task<ApiResult> GetRestaurantsByOutcode(string outcode)
{
return await _context.GetRestaurantsByOutcode(outcode);
}
// added async and await
public async Task<List<Restaurant>> GetSortedRestaurantsByOutcode(string outcode)
{
// here you were not using await but then using result even though you were calling into a method marked as async which in turn used an await. this is where you deadlocked but this the fix.
return (await _context.GetRestaurantsByOutcode(outcode)).Restaurants
.OrderBy(x => x.Name).ToList();
}
}

Call async await method in sync calling method

This is only the idea on what im doing in a window service.
I get the idea from this video to do it in parallel processing.
I have two different method and a model class.
Model Class code:
public class Email(){
public string Recipient { get; set; }
public string Message { get; set; }
}
Methods is something like this:
public void LoadData(){
while(Main.IsProcessRunning){
// 1. Get All Emails
var emails = new dummyRepositories().GetAllEmails(); //This will return List<Emails>.
// 2. Send it
// After sending assume that the data will move to other table so it will not be query again for the next loop.
SendDataParallel(emails);//this will function async? even though the calling method is sync.
// This will continue here or wait until it already send?
// If it will continue here even though it will not send already
// So there's a chance to get the email again for the next loop and send it again?
}
}
//This will send email at parallel
public async void SendDataParallel(IList<Email> emails){
var allTasks = emails.Select(SendDataAsync);
await TaskEx.WhenAll(allTasks);
}
//Assume this code will send email asynchronously. (this will not send email, for sample only)
public async void SendDataAsync(Email email){
using (var client = new HttpClient())
{
client.PostAsync(email);
}
}
I only want to get all queued emails then send it in parallel then wait until it already send.
I'm avoiding using foreach on every email that I get.
Lets start at the bottom:
You dispose your client before you actually finish receiving the HttpResponseMessage asynchronously. You'll need to make your method async Task and await inside:
public async Task SendDataAsync(Email email)
{
using (var client = new HttpClient())
{
var response = await client.PostAsync(email);
}
}
Currently, your SendDataParallel doesn't compile. Again, it needs to return a Task:
public Task SendEmailsAsync(IList<Email> emails)
{
var emailTasks = emails.Select(SendDataAsync);
return Task.WhenAll(allTasks);
}
At the top, you'll need to await on SendEmailsAsync:
public async Task LoadDataAsync()
{
while (Main.IsProcessRunning)
{
var emails = new dummyRepositories().GetAllEmails();
await SendEmailsAsync(emails);
}
}
Edit:
If you're running this inside a windows service, you can offload it to Task.Run and use the async keyword:
var controller = new Controller();
_processThread = Task.Run(async () => await controller.LoadDataAsync());
Doesn't your compiler highlight your code with errors?
If you mark your method as async while it doesn't return any value, you should set your return type as Task, not void:
public async Task SendDataParallel(IList<Email> emails){
var allTasks = emails.Select(SendDataAsync);
await Task.WhenAll(allTasks);
}
Your second method also shoud return a Task, otherwise what you want to (a)wait in the first method?
public async Task SendDataAsync(Email email){
using (var client = new HttpClient())
{
return client.PostAsync(email);
}
}
Now you can Select all your SendDataAsync tasks in SendDataParallel and .Wait() it's task in LoadData in synchronious mode:
public void LoadData(){
while(Main.IsProcessRunning){
var emails = new dummyRepositories().GetAllEmails(); //This will return List<Emails>.
SendDataParallel(emails).Wait();
}
}
More information you can find reading answers in other SO questions and docs on MSDN:
Can somebody please explain async / await?
Brief explanation of Async/Await in .Net 4.5
how to and when use async and await
Asynchronous Programming with Async and Await
And as you used LINQ's Select() which is based on foreach cycle next article also could be useful:
Nested task inside loop

Converting convenience methods that use Tasks

I often write code that has convenience methods which basically wrap other methods. Here's a simple example:
public class WithoutAsync
{
public static ReadOnlyCollection<Response> GetResponses(IEnumerable<Request> fromRequests)
{
var ret = new List<Response>();
foreach (Request r in fromRequests)
{
ret.Add(new Response());
}
return ret.AsReadOnly();
}
//convenience method
public static Response GetResponse(Request fromRequest)
{
return GetResponses(new Request[] {fromRequest})[0];
}
}
Now I want to await long-running operations but I can't quite figure out how to retrofit this methodology for use with TPL:
public class WithAsync
{
public static async Task<ReadOnlyCollection<Response>> GetResponses(IEnumerable<Request> fromRequests)
{
var awaitableResponses = new List<Task<Response>>();
foreach (Request r in fromRequests)
{
awaitableResponses.Add(Task.Run<Response>(async () =>
{
await Task.Delay(10000); //simulate some long running async op.
return new Response();
}));
}
return new List<Response>(await Task.WhenAll(awaitableResponses)).AsReadOnly();
}
//convenience method
public static Task<Response> GetResponse(Request fromRequest)
{
return GetResponse(new Request[] { fromRequest });
}
}
The convenience method above obviously won't work because it's trying to return a Task<ReadOnlyCollection<Response>> when it really needs to return a Task<Response>.
This works:
//convenience method
public static Task<Response> GetResponse(Request fromRequest)
{
return new Task<Response>(new Func<Response>(() => GetResponse(new Request[] { fromRequest }).Result[0]));
}
but it seems really awkward, and more importantly, it blocks on .Result[0] which is potentially on a UI thread.
Is there any good way to accomplish what I'm trying to do?
You're trying to avoid making that "convenience method" async, but there's no reason to do that.
What you want is to call the other method, wait until there are responses and then get the first and only one. You can do that by making it async and using await:
async Task<Response> GetResponseAsync(Request fromRequest)
{
var responses = await GetResponsesAsync(new[] { fromRequest });
return responses.Single();
}
Although a better solution in this specific case is to switch things around and have the single GetResponse actually do that work of a single request, and have the multiple GetRsponses call it instead:
async Task<ReadOnlyCollection<Response>> GetResponsesAsync(IEnumerable<Request> fromRequests)
{
return (await Task.WhenAll(fromRequests.Select(GetResponse))).ToList().AsReadOnly();
}
async Task<Response> GetResponseAsync(Request fromRequest)
{
await Task.Delay(10000); //simulate some long running async op.
return new Response();
}
Notes:
I know it's an example, but there's probably no reason to use Task.Run instead of a simple async call.
The convention is to name async methods with an "Async" suffix (i.e. GetResponseAsync).
I've also pluralized the name of the method that returns a collection.
I'm still sticking with I3arnon's answer because it's a well-written, informative answer, but I'd like to submit my own answer because I realized that I was almost there. Here's the async convenience method I was struggling to find:
//convenience method
public static async Task<Response> GetResponse(Request fromRequest)
{
return (await GetResponses(new Request[] { fromRequest }))[0];
}

Categories

Resources