So I'm trying to get my head around this new 'async' stuff in .net 4.5. I previously played a bit with async controllers and the Task Parallel Library and wound up with this piece of code:
Take this model:
public class TestOutput
{
public string One { get; set; }
public string Two { get; set; }
public string Three { get; set; }
public static string DoWork(string input)
{
Thread.Sleep(2000);
return input;
}
}
Which is used in a controller like this:
public void IndexAsync()
{
AsyncManager.OutstandingOperations.Increment(3);
Task.Factory.StartNew(() =>
{
return TestOutput.DoWork("1");
})
.ContinueWith(t =>
{
AsyncManager.OutstandingOperations.Decrement();
AsyncManager.Parameters["one"] = t.Result;
});
Task.Factory.StartNew(() =>
{
return TestOutput.DoWork("2");
})
.ContinueWith(t =>
{
AsyncManager.OutstandingOperations.Decrement();
AsyncManager.Parameters["two"] = t.Result;
});
Task.Factory.StartNew(() =>
{
return TestOutput.DoWork("3");
})
.ContinueWith(t =>
{
AsyncManager.OutstandingOperations.Decrement();
AsyncManager.Parameters["three"] = t.Result;
});
}
public ActionResult IndexCompleted(string one, string two, string three)
{
return View(new TestOutput { One = one, Two = two, Three = three });
}
This controller renders the view in 2 seconds, thanks to the magic of the TPL.
Now I expected (rather naively) that the code above would translate into the following, using the new 'async' and 'await' features of C# 5:
public async Task<ActionResult> Index()
{
return View(new TestOutput
{
One = await Task.Run(() =>TestOutput.DoWork("one")),
Two = await Task.Run(() =>TestOutput.DoWork("two")),
Three = await Task.Run(() =>TestOutput.DoWork("three"))
});
}
This controller renders the view in 6 seconds. Somewhere in the translation the code became no longer parallel. I know async and parallel are two different concepts, but somehow I thought the code would work the same. Could someone point out what is happening here and how it can be fixed?
Somewhere in the translation the code became no longer parallel.
Precisely. await will (asynchronously) wait for a single operation to complete.
Parallel asynchronous operations can be done by starting the actual Tasks but not awaiting them until later:
public async Task<ActionResult> Index()
{
// Start all three operations.
var tasks = new[]
{
Task.Run(() =>TestOutput.DoWork("one")),
Task.Run(() =>TestOutput.DoWork("two")),
Task.Run(() =>TestOutput.DoWork("three"))
};
// Asynchronously wait for them all to complete.
var results = await Task.WhenAll(tasks);
// Retrieve the results.
return View(new TestOutput
{
One = results[0],
Two = results[1],
Three = results[2]
});
}
P.S. There's also a Task.WhenAny.
No, you stated the reason that this is different already. Parallel and Async are two different things.
The Task version works in 2 seconds because it runs the three operations at the same time (as long as you have 3+ processors).
The await is actually what it sounds like, the code will await the execution of the Task.Run before continuing to the next line of code.
So, the big difference between the TPL version and the async version are that the TPL version runs in any order because all of the tasks are independent of each other. Whereas, the async version runs in the order that the code is written. So, if you want parallel, use the TPL, and if you want async, use async.
The point of async is the ability to write synchronous looking code that will not lock up a UI while a long running action is happening. However, this is typically an action that all the processor is doing is waiting for a response. The async/await makes it so that the code that called the async method will not wait for the async method to return, that is all. So, if you really wanted to emulate your first model using async/await (which I would NOT suggest), you could do something like this:
MainMethod()
{
RunTask1();
RunTask2();
RunTask3();
}
async RunTask1()
{
var one = await Task.Factory.StartNew(()=>TestOutput.DoWork("one"));
//do stuff with one
}
async RunTask2()
{
var two= await Task.Factory.StartNew(()=>TestOutput.DoWork("two"));
//do stuff with two
}
async RunTask3()
{
var three= await Task.Factory.StartNew(()=>TestOutput.DoWork("three"));
//do stuff with three
}
The code path will go something like this (if the tasks are long running)
main call to RunTask1
RunTask1 awaits and returns
main call to RunTask2
RunTask2 awaits and returns
main call to RunTask3
RunTask3 awaits and returns
main is now done
RunTask1/2/3 returns and continues doing something with one/two/three
Same as 7, except less the one that already completed
Same as 7, except less the two that already completed
****A big disclaimer about this, though. Await will run synchronously if the task is already completed by the time that the await is hit. This saves the runtime from having to perform its vudu :) since it is not needed. This will make the code flow above incorrect as the flow is now synchronous****
Eric Lippert's blog post on this explains things much better than I am doing :)
http://blogs.msdn.com/b/ericlippert/archive/2010/10/29/asynchronous-programming-in-c-5-0-part-two-whence-await.aspx
Hopefully, that helps dispel some of your questions about async versus TPL? The biggest thing to take away is that async is NOT parallel.
Related
I think my web application is suffering from deadlock when I'm calling YouTube API services, so I want to know how to resolve this in the correct manner. I suspect it's a similar scenario to the following: Why does this async action hang?
Please can somebody advise, in very simple terms, why my web application hangs (see inline comments) and how it should be correctly resolved? Thanks!
public ActionResult Index()
{
YouTubeHelper yth = new YouTubeHelper();
bool unpublishVideo = yth.UpdateVideoOnYouTube(17, "public").Result;
}
public async Task<bool> UpdateVideoOnYouTube(int propertyId, string publishStatus)
{
.....
YouTubeService youtubeService = await GetYouTubeService(db);
.....
}
public async Task<YouTubeService> GetYouTubeService(ApplicationDbContext db)
{
....
if (!await credential.RefreshTokenAsync(CancellationToken.None)) //It hangs here!!
{
....
}
....
}
The deadlock is explained here. In summary, your asynchronous methods are needing the ASP.NET request context before they can complete, but the call to Result is blocking the ASP.NET request context until the asynchronous methods are already completed.
To avoid the deadlock, don't block on async code. Use await instead of Result:
public async Task<ActionResult> Index()
{
YouTubeHelper yth = new YouTubeHelper();
bool unpublishVideo = await yth.UpdateVideoOnYouTube(17, "public");
}
I am using Edge.js so that I can call Node.js from C#. According to the documentation in the link I would do that similar to the following:
[HttpPost]
public ActionResult Input(InputModel obj)
{
validateInput(obj);
return View();
}
private async void validateInput(object obj)
{
var func = Edge.Func(#"
return function (data, callback){
var username = data.username,
email = data.email;
callback(null, username);
}
");
ViewBag.Msg = (string)await func(obj);
}
However, I get the following run time error:
Additional information: An asynchronous operation cannot be started at this time.
Asynchronous operations may only be started within an asynchronous handler or module or during
certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the
Page is marked <%# Page Async="true" %>. This exception may also indicate an attempt to call an
"async void" method, which is generally unsupported within ASP.NET request processing. Instead,
the asynchronous method should return a Task, and the caller should await it.
My question is two-fold:
1.How do I make the page, async=true. I know how to do this for a web forms project but not a MVC project.
2.Is there a better way to do what I am trying to do? A red flag will probably go up when you see that I am returning void however this is do to the fact that Edge.js is being used. Even so, I have tried returning a Task and then task.Wait() in the calling method but the task never finishes.
After trying some different things, the following solution worked for me.
Even though I answered my own question, and it seems trivial, I am not removing this question as there are not a lot of knowledge on the web about Edge.js.
[HttpPost]
public async Task<ActionResult> Input(InputModel obj)
{
ViewBag.Msg = await validateInput(obj);
return View();
}
private async Task<string> validateInput(object obj)
{
var func = Edge.Func(#"
return function (data, callback){
var username = data.username,
email = data.email;
callback(null, username);
}
");
return (string)await func(obj);
}
I am trying to write a multi threading search and then display all the results once the tasks have finished running but currently I don't understand how to process the results once all the tasks are complete
My code is as follows:
private async void DoSearchAsync()
{
var productResults = await SearchProductsAsync(CoreCache.AllProducts);
var brochureResults = await SearchBrochuresAsync(CoreCache.AllBrochures);
_searchResults.AddRange(productResults);
_searchResults.AddRange(brochureResults);
ResultsCount = _searchResults.Count;
}
Where _searchResults is a List<SearchResult>
My understanding is that it will do both of the awaits simultaneously and then add the products to the search results. However when I call this in my controller:
public ActionResult Index(string searchText)
{
SearchHelper helper = new SearchHelper(searchText);
helper.DoSearchAsync();
return View(helper);
}
It is displaying the page before the searches are complete so no results are showing up. How do I make it wait for the results to finish before showing the page?
I've had a look into Tasks.Wait but don't know how to apply it to the above as it expects an array of tasks
private Task<List<SearchResult>> SearchProductsAsync(IEnumerable<Product> products)
{
return Task<List<SearchResult>>.Factory.StartNew(() => GetProducts(products));
}
private Task<List<SearchResult>> SearchBrochuresAsync(IEnumerable<Assets> brochures)
{
return Task<List<SearchResult>>.Factory.StartNew(() => GetBrochures(brochures));
}
Every time you call Factory.StartNew or Task.Run inside an ASP.NET controller, you grab a thread from ThreadPool. That thread could be otherwise serving another incoming HTTP request. So, your're really hurting your web app scalability. This may be a serious issue, depending on how many concurrent HTTP requests your web app is expected to receive.
Are you OK with that? If so, the code could look like this:
private async Task DoSearchAsync()
{
var productResults = SearchProductsAsync(CoreCache.AllProducts);
var brochureResults = SearchBrochuresAsync(CoreCache.AllBrochures);
await Task.WhenAll(productResults, brochureResults);
_searchResults.AddRange(productResults.Result);
_searchResults.AddRange(brochureResultsbrochure.Results);
ResultsCount = _searchResults.Count;
}
public async Task<ActionResult> Index(string searchText)
{
SearchHelper helper = new SearchHelper(searchText);
await helper.DoSearchAsync();
return View(helper);
}
Note I changed async void to async Task for DoSearchAsync, and made your controller method async, so it returns Task<ActionResult>.
My understanding is that it will do both of the awaits simultaneously
This is not correct Pete. The await means that it will pause execution of the current method until such time that the Async method being called completes. So your 2 searches will not run in Parallel.
For clarification, see the first line of the MSDN documentation....
The await operator is applied to a task in an asynchronous method to suspend the execution of the method until the awaited task completes.
You should use Task.WhenAll to wait for both searches to complete. As WhenAll supports returning Task<T> and both of your Async searches return a List<SearchResult> you can combine the results of both searches and await them both in one statement...
var searchProducts = Task.Factory.StartNew(() => GetProducts(CoreCache.AllProducts));
var searchBrochure = Task.Factory.StartNew(() => GetBrochures(CoreCache.AllBrochures));
var allResults = await Task.WhenAll(new [] { searchProducts, searchBrochure });
//allResults is List<SearchResult>[]
For completeness, its worth noting that allResults will contain 2 result sets. It does not perform a union on the 2 sets.
A complete working example for LinqPad can be found here on GitHub
You can use a task factory, then call wait on the resulting task:
Task taskA = Task.Factory.StartNew(() => DoSomeWork(10000000));
taskA.Wait();
Console.WriteLine("taskA has completed.");
http://msdn.microsoft.com/en-us/library/dd537610(v=vs.110).aspx
You can make it wait for the results to finish before displaying the page by making your function NOT be async.
Or if you want async you could move the search functions to be called by an AJAX call triggered when your page is loaded
You need to wait for the function to complete. In order to do that, first make it return a task:
private async Task DoSearchAsync()
then await it:
public async Task<ActionResult> Index(string searchText)
{
SearchHelper helper = new SearchHelper(searchText);
await helper.DoSearchAsync();
return View(helper);
}
For more information, see http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4
I have an MVC web application, the controller action calls a code in a business layer, which in turn calls an API:
public async Task<ActionResult> TestAction(){
Info something = await businessLayer.GetInfoAsync();
return View(something);
}
in the business layer I have GetInfoAsync defined as follows:
public async Task<Info> GetInfoAsync(){
return await myLib.GetAsync<Info>();
}
in my library I have GetAsync defined as follows:
public async Task<T> GetAsync(){
//do something
var data = await aService.Get<T>();
return data;
}
As you can see above GetInfoAsync method just makes a call to myLib.GetAsync and returns whatever value myLib.GetAsync returns.
Could I just get rid of async/await for GetInfoAsync, and define it as follows :
public Task<Info> GetInfoAsync(){
return myLib.GetAsync<Info>();
}
Thanks !
Yes, you can (and should) get rid of async/await in the middle layer.
If your async method is just awaiting a single task (and that's all it does), then the async just adds overhead.
I have a system whereby users can upload sometimes large(100-200 MB) files from within an MVC3 application. I would like to not block the UI while the file is uploading, and after some research, it looked like the new AsyncController might let me do what I'm trying to do. Problem is - every example I have seen isn't really doing the same thing, so I seem to be missing one crucial piece. After much futzing and fiddling, here's my current code:
public void CreateAsync(int CompanyId, FormCollection fc)
{
UserProfile up = new UserRepository().GetUserProfile(User.Identity.Name);
int companyId = CompanyId;
// make sure we got a file..
if (Request.Files.Count < 1)
{
RedirectToAction("Create");
}
HttpPostedFileBase hpf = Request.Files[0] as HttpPostedFileBase;
if (hpf.ContentLength > 0)
{
AsyncManager.OutstandingOperations.Increment();
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, e) =>
{
string fileName = hpf.FileName;
AsyncManager.Parameters["recipientId"] = up.id;
AsyncManager.Parameters["fileName"] = fileName;
};
worker.RunWorkerCompleted += (o, e) => { AsyncManager.OutstandingOperations.Decrement(); };
worker.RunWorkerAsync();
}
RedirectToAction("Uploading");
}
public void CreateCompleted(int recipientId, string fileName)
{
SystemMessage msg = new SystemMessage();
msg.IsRead = false;
msg.Message = "Your file " + fileName + " has finished uploading.";
msg.MessageTypeId = 1;
msg.RecipientId = recipientId;
msg.SendDate = DateTime.Now;
SystemMessageRepository.AddMessage(msg);
}
public ActionResult Uploading()
{
return View();
}
Now the idea here is to have the user submit the file, call the background process which will do a bunch of things (for testing purposes is just pulling the filename for now), while directing them to the Uploading view which simply says "your file is uploading...carry on and we'll notify you when it's ready". The CreateCompleted method is handling that notification by inserting a message into the users's message queue.
So the problem is, I never get the Uploading view. Instead I get a blank Create view. I can't figure out why. Is it because the CreateCompleted method is getting called which shows the Create view? Why would it do that if it's returning void? I just want it to execute silently in the background, insert a message and stop.
So is this the right approach to take at ALL? my whole reason for doing it is with some network speeds, it can take 30 minutes to upload a file and in its current version, it blocks the entire application until it's complete. I'd rather not use something like a popup window if I can avoid it, since that gets into a bunch of support issues with popup-blocking scripts, etc.
Anyway - I am out of ideas. Suggestions? Help? Alternate methods I might consider?
Thanks in advance.
You are doing it all wrong here. Assume that your action name is Create.
CreateAsync will catch the request and should be a void method and returns nothing. If you have attributes, you should apply them to this method.
CreateCompleted is your method which you should treat as a standard controller action method and you should return your ActionResult inside this method.
Here is a simple example for you:
[HttpPost]
public void CreateAsync(int id) {
AsyncManager.OutstandingOperations.Increment();
var task = Task<double>.Factory.StartNew(() => {
double foo = 0;
for(var i = 0;i < 1000; i++) {
foo += Math.Sqrt(i);
}
return foo;
}).ContinueWith(t => {
if (!t.IsFaulted) {
AsyncManager.Parameters["headers1"] = t.Result;
}
else if (t.IsFaulted && t.Exception != null) {
AsyncManager.Parameters["error"] = t.Exception;
}
AsyncManager.OutstandingOperations.Decrement();
});
}
public ActionResult CreateCompleted(double headers1, Exception error) {
if(error != null)
throw error;
//Do what you need to do here
return RedirectToAction("Index");
}
Also keep in mind that this method will still block the till the operation is completed. This is not a "fire and forget" type async operation.
For more info, have a look:
Using an Asynchronous Controller in ASP.NET MVC
Edit
What you want here is something like the below code. Forget about all the AsyncController stuff and this is your create action post method:
[HttpPost]
public ActionResult About() {
Task.Factory.StartNew(() => {
System.Threading.Thread.Sleep(10000);
if (!System.IO.Directory.Exists(Server.MapPath("~/FooBar")))
System.IO.Directory.CreateDirectory(Server.MapPath("~/FooBar"));
System.IO.File.Create(Server.MapPath("~/FooBar/foo.txt"));
});
return RedirectToAction("Index");
}
Notice that I waited 10 seconds there in order to make it real. After you make the post, you will see the it will return immediately without waiting. Then, open up the root folder of you app and watch. You will notice that a folder and file will be created after 10 seconds.
But (a big one), here, there is no exception handling, a logic how to notify user, etc.
If I were you, I would look at a different approach here or make the user suffer and wait.