I want to create a async method which will open a thread and complete several tasks that is relatively independent to the current http request (for example, sending emails and generating files both of which takes time)
I created the following method
private static async Task<T> DoSeparateTask<T>(Func<T> func)
{
return func();
}
and want to use it in such a way:
private static void DoSomething()
{
#step 1 - some code for immediate processing
#step 2 - some code for generating documents which takes time
var additionalDocumentGenerationWork = DoSeparateTask<Document>(() =>{
#additional code for generating Document
#even though it returns Document, but 99% time it wont be used in this method
});
#step 3 - some code for sending SMTP email which takes time
var additionalDocumentGenerationWork = DoSeparateTask<bool>(() =>{
#additional code for sending email
return true;
});
}
Everything compiles, however when I run the web application, it still keeps loading and waiting everything to complete before rendering the web page. As the 2 additional tasks (email and documents) are not relevant for displaying the webpage, how can I achieve such by using the async modifiers?
Or if my understanding of async is wrong... please help me to correct...
Many thanks.
Or if my understanding of async is wrong... please help me to correct...
As I explain on my blog, async does not change the HTTP protocol. At the end of the day, you only have one response for each request. To put another way, async yields to the thread pool, not to the client.
What you're looking for is a way to do operations outside the request/response lifecycle, which is not directly supported by ASP.NET. I describe a few approaches on my blog. The best (most reliable) approach is to place the work into a queue, and have an independent backend that processes the queue (such as an Azure worker role).
You don't need the DoSeparateTask<T>() method. Instead, just use Task.Run<T>():
private static async Task DoSomething()
{
// #step 1 - some code for immediate processing
// #step 2 - some code for generating documents which takes time
var additionalDocumentGenerationWork1 = await Task.Run(() =>
{
// #additional code for generating Document
// #even though it returns Document, but 99% time it wont be used in this method
});
// #step 3 - some code for sending SMTP email which takes time
var additionalDocumentGenerationWork2 = await Task.Run(() =>
{
// #additional code for sending email
return true;
});
}
Note that the type of additionalDocumentGenerationWork1 and additionalDocumentGenerationWork2 (you used the same name twice in your example, so I changed it above to make the code legal) are Document and bool respectively. You can use additionalDocumentGenerationWork1 in your second task invocation if you like (e.g. to pass the result of the first operation).
Related
I have an MVC controller method that does a number of things, but the last thing it does is this:
public void PerformCheckout(int salesAddressId)
{
try
{
...
...
// We just made a sale. Send emails to all market owners.
SendSalesEmailsAsync(master);
}
catch (System.Exception exception)
{
// Log the error
}
And then SendSalesEmailesAsynch() looks like this:
private async Task SendSalesEmailsAsync(SalesMaster salesMaster)
{
...
...
// Send an email to the owner of each marker
foreach(string market in markets)
await SendSalesEmailAsync(salesMaster, salesDetailList, market).ConfigureAwait(false);
}
SendSalesEmailAsynch() looks like this:
// Send an email to a market owner
private async Task SendSalesEmailAsync(SalesMaster salesMaster, List<SalesDetail> salesDetail, string marketName)
{
...
...
Email email = new Email(new List<string> { sellerEmailAddress }, emailSubject, emailHtmlBody);
await email.SendAsync().ConfigureAwait(false);
And finally, the method that actually sends the email:
public async Task SendAsync()
{
// Create a network credentials object
var credentials = new NetworkCredential(azureUserName, azurePassword);
// Create an Web transport for sending the email
var transportWeb = new Web(credentials);
// Send the email. We don't care about the current context so let's run this in a thread pool context.
// For more information: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
await transportWeb.DeliverAsync(this._email).ConfigureAwait(false);
}
I am pretty new with Async and Await. Does all this look proper? Also, I'm not entirely sure that the catch block in the controller action method will get called if an error occurs while sending an email.
As you suspected, the catch block won't get any exceptions since you didn't synchronize with current context. In order to do that you have to mark your action method as async and use await for calling SendSalesEmailsAsync. So the action method will look like this:
public async Task PerformCheckout(int salesAddressId)
{
try
{
...
...
// We just made a sale. Send emails to all market owners.
await SendSalesEmailsAsync(master);
}
catch (System.Exception exception)
{
// Log the error
}
This is not the typical approach used for async await. I would not feel comfortable saying "incorrect" because design goals are so implementation dependent.
There are several issues that show up in the code shown. Having a method which takes an async action and returns void is probably a bad idea for a few reasons.
Controllers shouldn't really be exposing functionality that doesn't include returning information. If it is an action to perform, it should be called as some sort of service or business logic. Furthermore, since the method is public and part of the controller anyone can invoke it from the url. As it is sending a group of emails, this may be undesirable.
Async/await should be used to free up processing power. For the most part, it should only be used when either the processing time or waiting time exceeds 40 milliseconds. As emails are fire and forget, this may not be the case in your situation in which case the entire method could be converted back to synchronous.
If it is taking longer than 40ms, then async is going to be okay to use. However, in the shown code the first call is not properly used. When an async call is made without await it is basically "fire and forget". Especially when there is no return value, the return is never noticed. As a result, if an exception is thrown it is also forgotten, and the catch block will never executed.
Using await causes a new task continuation to be created. The rest of the method is essentially forked while the call takes place. Once the called execution has finished, then the rest of the method which was forked executes. Without await, the rest of the method simply executes, and the execution of the side call probably finishes far later.
All in all, this design should probably be refactored to be called from a more controlled environment, and possibly even from a synchronous method.
Background: We have import-functions that can take anywhere from a few seconds to 1-2 hours to run depending on the file being imported. We want to expose a new way of triggering imports, via a REST request.
Ideally the REST service would be called, trigger the import and reply with a result when done. My question is: since it can take up to two hours to run, is it possible to reply or will the request timeout for the caller? Is there a better way for this kind of operation?
What I use in these cases is an asynchronous operation that returns no result (void function result in case of c# Web API), then send the result asynchronously using a message queue.
E.g.
[HttpPut]
[Route("update")]
public void Update()
{
var task = Task.Run(() => this.engine.Update());
task.ContinueWith(t => publish(t, "Update()"));
}
Im dealing with a scenario in C#.net where Im loading a page, and during that pageload event, a service is being called to populate a value which is used to diplay on the page.
The page also has other fields. So the issue here is, the service usually takes between 30 to 60 secs to return the value, other fields cannot be selected until this service returns a value. So there is also a "SAVE" button which cannot be clicked since we are still waiting on this service to return the value.
The problem im trying to solve here is, ideally, on pageload, I want this service to run on the background and let other fields populate the value and I should be able to execute other events like SAVE, NEXT PAGE, PREVIOUS PAGE etc when called.
The purpose of the value returned by the service is for reference only. The code does not have any dependency on this value.
If your service has a method call it by making your onpageload callback async and await the asynchronous method inside of it.
If the service does not have an async method to call you can create your own e.g:
public string DownloadStringAsync()
{
return "Test data";
}
public async Task<string> ReceiveStringAsync()
{
return await Task<string>.Run(() =>
{
//Method to download your data
return DownloadStringAsync();
});
}
and await it in your page load: sting data = await ReceiveStringAsync();
You are not providing enough information/code for a better answer.
You can try executing it in a Thread. Something like this
Thread runner = new Thread(() =>
{
// run service call here
});
runner.IsBackground = true;
runner.Start();
But in C# 4.0 there is the new Task Parallelism Library so you can read about that and proceed from there, technically a Task is like a thread, but the difference is that the Task can have continuation aswell as it manages the Thread pool.
Read TPL here: Task Parallelism Library
I've worked with concurrency, but I don't know any good way to test it.
I would want to know if there is any way to "force" Tasks to execute in an specific order to simulate tests cases.
For example:
Client #1 makes a request
Server starts retrieving data to Client #1
Client #2 makes another request while the server is still responding to Client #1
Assert << something >>
I have seen some people using custom TaskSchedulers. Does it make sense?
I had had that problem in several occurrences too. Eventually I have created a helper that can start a bunch of threads to execute concurrent actions. The helper provides synchronization primitives and logging mechanisms. Here is a code fragment from a unit test:
[Test]
public void TwoCodeBlocksInParallelTest()
{
// This static method runs the provided Action delegates in parallel using threads
CTestHelper.Run(
c =>
{
Thread.Sleep(1000); // Here should be the code to provide something
CTestHelper.AddSequenceStep("Provide"); // We record a sequence step for the expectations after the test
CTestHelper.SetEvent();
},
c =>
{
CTestHelper.WaitEvent(); // We wait until we can consume what is provided
CTestHelper.AddSequenceStep("Consume"); // We record a sequence step for the expectations after the test
},
TimeSpan.FromSeconds(10)); // This is a timeout parameter, if the threads are deadlocked or take too long, the threads are terminated and a timeout exception is thrown
// After Run() completes we can analyze if the recorded sequence steps are in the correct order
Expect(CTestHelper.GetSequence(), Is.EqualTo(new[] { "Provide", "Consume" }));
}
It can be used to test client/server or synchronization in components or simply run a thread with a timeout. I'll continue to improve this in the next weeks. Here is the project page:
Concurrency Testing Helper
This shouldn't be too difficult to simulate using tasks:
private async Task DoSomeAsyncOperation()
{
// This is just to simulate some work,
// replace this with a usefull call to the server
await Task.Delay(3000);
}
Now, lets consume it:
public async Task TestServerLoad()
{
var firstTaskCall = DoSomeAsyncOperation();
await Task.Delay(1000); // Lets assume it takes about a second to execute work agains't the server
var secondCall = DoSomeAsyncOperation();
await Task.WhenAll(firstTaskCall, secondCall); // Wait till both complete
}
This is basic producer-consumer problem in concurrency. If you want to test that case, just put a Thread.Sleep(100) to server which part responds consumers. In that way, your server will have a delay before sending response. And you can Invoke service request simply creating new threads in a loop.
I'm putting together a simple C# app where a user types commands into a "command bar" and the results are fetched in real time.
For example I could type, google stackoverflow, and it sends an API call off to google, fetches each of the results and displays them in the ui.
Currently I fire off the "search" method if a user pauses for more than 1/4 of a second on typing, so if you paused in the middle it could fire google stack and google stackoverflow.
Now in reality the api is doing a (rather slow) database query and this causes the ui to lock up while the first search completes, before it tries to start on the second search.
Is there a simple (C# 4.0) way to run the search call in a separate thread that I can then cancel/abort if the user continues typing?
e.g.
Task<string> thread;
string getSearchResults(string input) {
... Do some work ...
}
string userPaused(string search) {
if(this.thread.isRunning()) this.thread.Kill();
this.thread = new Task<String>(getSearchResults(string input);
return this.thread.result();
}
I've looked at the Tasks api and it doesn't look as if you can kill a task in the middle of work, it suggests using a while look and passing a shouldStop boolean, however during an API call to download the results there is no while loop.
The Threading documentation however points you to tasks if you need to get the return value.
What you may do with Tasks is to create them, and then cancel when not needed any more. If you can't cancel operation you are doing (like database query) - you can always cancel before results get returned. Your code may be something like this (not tested, just a draft):
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = tokenSource2.Token;
var task = Task.Factory.StartNew(() =>
{
ct.ThrowIfCancellationRequested();
var result = Database.GetResult(); // whatever database query method you use.
ct.ThrowIfCancellationRequested();
return result;
}, tokenSource2.Token);
So as you can see it will query database and return value when no cancellation requested, but if you will try to cancell the task - it will not return value but rather throw OperationCanceledException you need to catch. For details visit MSDN Task Cancellation, but I think this should give you an idea. Don't worry about big task number - if your query is not very slow it won't matter - user will not be able to trigger so many searches. If you have asynchronous way of querying database - you can improve this code a bit more, but that also shouldn't be too hard.