I have the below method in my FileController:
[HttpPost]
[NoAsyncTimeout]
public ActionResult FileGenerator(FileViewModel model) {
...
_fileGenerator.CreateFile(model.Id,model.location);
...
}
The method CreateFile is defined below:
public void CreateFile(int residentId, string location){
...
var caller = new AsyncMethodCaller(GenerateFilesAsync);
caller.BeginInvoke(residentId,location, new
AsyncCallback(GenerateFilesCallBack));
...
}
From what I know, AsyncMethodCaller and BeginInvoke is to process the file creation into a different
Thread so that it does not block the interface. The above method is being called by a web application
and several users can make requests to the controller Action FileGenerator - this will process the
file generation concurrently (may be in multiple thread).
I want to implement a mechanism to process the requests on a FIFO basis - i.e if a user accesses the web application
and invokes the function FileGenerator, while this file is being generated - another user from a different PC invokes
the function at the same time, his request should be queued and executed after the first user file is generated.
Is there any mechanism or method in MVC to queue controller Action requests from users?
You can leverage the Queue Class in C#. Instead of spawning an new thread for each request I would suggest using a single thread to monitor the queue and call the FileGenerator method to create file. You will also not need to make it async in this case.
So whenever a user access the controller method, it will place the model into queue. The thread which is monitoring the queue will process it whenever it has some models in it.
This is probably a crude way for Queue Management :)
Better way would be to use a standard Queuing Mechanisms MSMQ, but then again depends on the complexity of the application and required scalability.
Related
This question already has answers here:
Wraping sync API into Async method
(2 answers)
Closed 1 year ago.
I read some posts and articles on the internet saying that I should not use async until my calls are IO (reading file, sending request, etc.). But if the calling API is actually an IO-related API and does not support async calls, should I wrap them with Task.Run()?
Say, if I have MVC server calling an external server via API to get data, but the API does not have async methods. Is there a benefit wrapping sync calls with async in such case?
As I understand, if not wrapping, then all calls to the API are sync and each call to my MVC app uses a thread from thread pool. In second case Task.Run() will queue my task onto thread pool, but releases main thread. Is released main thread considered to be benefit here, or it is not worth wrapping? Am I correct here at all?
EDIT
Ok, here are some more details as people requested.
I have a third party .NET assembly that I'm forced to use. I can't just call the web-service directly. It looks pretty much like this:
// the API (I not owner of it, can't change it):
public class Service {
/* everything is synchronous like this. but somewhere inside
it still makes HTTP requests to external server, so that's
kinda IO. just the lib is very old and doesn't provide async methods*/
public Data GetData(Paramters parameters);
}
// here comes my code.
// option 1. sync controller
public class MyController {
public ActionResult GetDataById(string id) {
var data = new Service().GetData(new Parameters{Id = id});
// process and return some result ...
}
}
// option 2. async controller
public class MyController {
public async ActionResult GetDataById(string id) {
var result = await Task.Run(() => new Service().GetData(new Parameters{Id = id}));
// process and return some result ...
}
}
So the question is, does it make sense to do option 2?
By wrapping into async, you may get the benefit of making parallel calls to the external server while the actual call would still remain sync , so it would be like multiple threads each waiting on response from external server. I have done this and it could be beneficial when you would like to handle the failures and then the application would not remain stuck in the calling threads.
Let's say if the API itself performs IO related tasks even then having a free main thread would benefit you if the user chooses to perform multiple tasks through the API you would benefit by having the main thread free rather than having it blocked on a single request.
I have a program, that takes long time to initialize but it's execution is rather fast.
It's becoming a bottleneck, so I want to start multiple instances of this program (like a pool) having it already initialized, and the idea is to just pass the needed arguments for it's execution, saving all the initialization time.
The problem is that I only found howto start new processes passing arguments:
How to pass parameters to ThreadStart method in Thread?
but I would like to start the process normally and then be able to communicate with it to send each thread the needed paramenters required for it's execution.
The best aproach I found was to create multiple threads where I would initialize the program and then using some communication mechanism (named pipes for example as it's all running in the same machine) be able to pass those arguments and trigger the execution of the program (one of the triggers could break an infinite loop, for example).
I'm asking if anyone can advice a more optimal solution rather that the one I came up with.
I suggest you don't mess with direct Thread usage, and use the TPL, something like this:
foreach (var data in YOUR_INITIALIZATION_LOGIC_METHOD_HERE)
{
Task.Run(() => yourDelegate(data), //other params here);
}
More about Task.Run on MSDN, Stephen Cleary blog
Process != Thread
A thread lives inside a process, while a process is an entire program or service in your OS.
If you want to speed-up your app initialization you can still use threads, but nowadays we use them on top of Task Parallel Library using the Task Asynchronous Pattern.
In order to communicate tasks (usually threads), you might need to implement some kind of state machine (some kind of basic workflow) where you can detect when some task progress and perform actions based on task state (running, failed, completed...).
Anyway, you don't need named pipes or something like that to communicate tasks/threads as everything lives in the same parent process. That is, you need to use regular programming approaches to do so. I mean: use C# and thread synchronization mechanisms and some kind of in-app messaging.
Some very basic idea...
.NET has a List<T> collection class. You should design a coordinator class where you might add some list which receives a message class (designed by you) like this:
public enum OperationType { DataInitialization, Authentication, Caching }
public class Message
{
public OperationType Operation { get; set; }
public Task Task { get; set; }
}
And you start all parallel initialization tasks, you add everyone to a list in the coordinator class:
Coordinator.Messages.AddRange
(
new List<Message>
{
new Message
{
Operation = Operation.DataInitialization,
Task = dataInitTask
},
..., // <--- more messages
}
);
Once you've added all messages with pending initialization tasks, somewhere in your code you can wait until initialization ends asynchronously this way:
// You do a projection of each message to get an IEnumerable<Task>
// to give it as argument of Task.WhenAll
await Task.WhenAll(Coordinator.Messages.Select(message => message.Task));
While this line awaits to finish all initialization, your UI (i.e. the main thread) can continue to work and show some kind of loading animation or who knows what (whatever).
Perhaps you can go a step further, and don't wait for all but wait for a group of tasks which allow your users to start using your app, while other non-critical tasks end...
I have a form post which should call an action asynchronously.
public async Task<ActionResult> SaveW2Document(Models.DocumentData documentData)
{
some code here
var ID = await Task.Run<int>(() => somObject.SaveDocument(uploadDocument));
//Code that uses ID
if(ID == something){
//Do something
}
}
However it gets called synchronously and my other actions do not execute until this action gets completed.Is it something else that I need to do?
I have a form post which should call an action asynchronously.
I think you're misunderstanding what "asynchronous" means in this context. Using async controller actions (or HTTP request handlers in general) means that the web server can invoke the operations asynchronously and not hold a thread in a wait state while the operation completes. This allows the web server to handle more concurrent requests more effectively.
The browser, however, is waiting for the response. The asynchronous nature of any given operation generally isn't defined by the operation itself, but by whatever invokes it. (For example, note how within this operation there is another asynchronous operation being awaited. If this code didn't await it, the behavior would be very different. But the method being invoked wouldn't change between those two scenarios.)
This makes sense, though. After all, what is the browser going to do if it doesn't wait for the response from the server? Display a blank page?
You can make asynchronous calls to a web server from client-side code by way of using AJAX. That would allow the page to invoke the server-side operation and listen for the response without blocking or changing the context of the page itself. But if the whole page context is posting to the server and waiting to load a new page, it needs to receive that new page before it can display it.
Well I might not have been able to ask the question correctly.But I found the solution to the problem that was occuring. MVC has a behaviour that blocks concurrent requests coming from the same session.I will either have to Disable Session or make it readonly for that controller. Refer this link.
So adding [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)] to my controller helped.
I am currently trying to write some code that will accept some FTP details, get a file list, then allow the user to download the files.
This all works fine, except that I have to wait for the files to finished downloading before it will do anything. I am using an Asynchronous controller as I thought this was meant to help with this sort of thing.
Please see a selection of my code below, I have left out all the code thats not relevant:
[HttpPost]
public ActionResult FtpAsync(Downloads model)
{
var ftpAddress = model.Url;
if (!ftpAddress.StartsWith("ftp://"))
ftpAddress = String.Concat("ftp://", ftpAddress);
var serverPath = Server.MapPath(ThumbnailSupport.CreateBaseVirtualPathForClient(StandardFileLinks.DropBoxLocation,
_website.Client));
if (!Directory.Exists(serverPath))
Directory.CreateDirectory(serverPath);
foreach (string file in model.SelectedFiles)
{
var webClient = new WebClient();
AsyncManager.OutstandingOperations.Increment();
AsyncManager.Parameters["FilesToDownload"] = model.SelectedFiles;
webClient.Credentials = new NetworkCredential(model.Username, model.Password);
webClient.DownloadFileAsync(new Uri(ftpAddress + "/" + file), serverPath + "\\" + file);
AsyncManager.OutstandingOperations.Decrement();
}
return RedirectToAction("Transfer");
}
public ActionResult FtpCompleted(Downloads model)
{
return RedirectToAction("Complete");
}
public ActionResult Transfer()
{
return PartialView();
}
It fires the FtpCompleted action perfectly fine, but the problem is this is meant to handle file transfers of potentially GB's of information. I dont want the user to sit watching a spinning disc while they wait for the files to be downloaded. Hence the reason I was trying to redirect them to the Transfer Action, this action just displays a message telling them that the transfer may take a while and they will be notified once its complete. However this action never actually gets called. I step through the code in debug and it calls it, but it never displays the message and it never gets to the browser according to FireBug.
Am I doing something stupid or is it just not possible to do this?
I would be grateful for any assistance people can offer here as I am completely stuck after browsing google and other posts on here. Even my boss who is a much more experienced coder isnt sure how to handle this.
Thanks in advance,
Gaz
As explained in the documentation an asynchronous controller action consists of 2 methods:
A method which returns void and has the Async suffix and the action name prefix
A method which returns ActionResult and has the Completed suffix and the action name prefix
The first method triggers asynchronous operation and returns immediately. The second method is invoked once the entire operation has completed.
So here's are the correct action signatures:
[HttpPost]
public void FtpAsync(Downloads model)
{
...
}
public ActionResult FtpCompleted(Downloads model)
{
return Content("Complete");
}
Now, if you don't want to freeze the browser during the entire operation you will have to invoke the first controller action using AJAX. Asynchronous controllers do not change anything in terms of the HTTP protocol. From the client perspective it is absolutely the same. The only difference with a standard controller action is that you are not jeopardizing a worker thread during the entire operation. You are now relying on the IOCP (IO/Completion Ports) that are an OS artifact used by the WebClient. The idea is that when you start an IO intensive operation, an IOCP port is created and the controller action immediately returns the thread to the ASP.NET thread pool. Then the operation could last for hours, and once it completes the IOCP is signaled, a pool is drawn from the thread pool and the Completed action is invoked on this thread. So in terms of threads it is very effective. But the total time of execution is absolutely the same as if you have used a standard controller action. Many people think that because Asynchronous Controller are called Asynchronous they run asynchronously from the client perspective. That's a false impression. Asynchronous actions don't make your operations miraculously run faster. They are still perfectly synchronous from the client perspective, because that's how the HTTP protocol works.
So you have 2 possibilities:
Invoke the controller action using AJAX to avoid blocking the browser.
Use COMET/PUSH technology in which it is the server that notifies the client. For example in HTML5 there's the WebSockets standard which could be used to achieve that. In the .NET world SignalR is a great framework which encapsulates this PUSH technology.
The second approach is recommended if you want a really effective solution.
I'm trying to convert this method ExportTo3rdParty() to use an AsyncController:
public JsonResult SaveSalesInvoice(SalesInvoice invoice)
{
SaveInvoiceToDatabase(invoice); // this is very quick
ExportTo3rdParty(invoice); // this is very slow and should be async
}
But the ExportTo3rdParty() method uses HttpContext.Current in multiple places (far too many to change - the original coder did not use enough dependency injection). For example it calls GetDefaultCurrency(). Will this still work when ExportTo3rdParty() is called through an AsyncController?
public Currency GetDefaultCurrency()
{
Currency currency;
string key = string.Format("DefaultCurrency_{0}",
HttpContext.Current.User.Identity.Name);
currency = HttpRuntime.Cache.Get(key) as Currency;
if (currency == null)
{
currency = LookupDefaultCurrency();
HttpRuntime.Cache[key] = currency;
}
}
I know that if I use Thread.Start that I can not access HttpContext.Current. But what about an AsyncController?
So let me ask you why you want to use an Async controller?
Do you think it's going to be faster? Just because something it slow doesn't mean you need to make it async. In fact you'll most likely find that the method is slower when run async due to thread management/context switching overheads.
From what little I can understand from the two methods you've shown. I'm guessing that ExportTo3Party can basically be done "out of band". That is by an external process. So what you should be doing is either use MSMQ to queue the job (this returns immediately) so it's non-blocking. And have some other process/application process the queued jobs. This other process could be a regular Console application that is kept running on the server (using Task Sheduler) and it simply processes jobs as soon as they arrive in the queue.
Or even simpler (if you've not used MSMQ), simply execute an external application (console app again) and not wait for the app to exit. So you can use System.Diagnostics.Process to start the process and don't WaitForExit.
Both of these alternatives are the right/better way to implement what I think ExportTo3rdParty is doing. Seeing that you're not waiting on a response from this method ot return it.
If I haven't convinced you yet, then:
From MSDN documentation
If an asynchronous action method calls
a service that exposes methods by
using the BeginMethod/EndMethod
pattern, the callback method (that is,
the method that is passed as the
asynchronous callback parameter to the
Begin method) might execute on a
thread that is not under the control
of ASP.NET. In that case,
HttpContext.Current will be null, and
the application might experience race
conditions when it accesses members of
the AsyncManager class such as
Parameters. To make sure that you have
access to the HttpContext.Current
instance and to avoid the race
condition, you can restore
HttpContext.Current by calling Sync()
from the callback method.
If the callback completes
synchronously, the callback will be
executed on a thread that is under the
control of ASP.NET and the operations
will be serialized so there are no
concurrency issues. Calling Sync()
from a thread that is already under
the control of ASP.NET has undefined
behavior.
The ActionCompleted method will always
be called on a thread that is under
the control of ASP.NET. Therefore, do
not call fSync() from that method.
The callback that you pass to the
Begin method might be called using a
thread that is under the control of
ASP.NET. Therefore, you must check for
this condition before you call Sync().
If the operation completed
synchronously (that is, if
CompletedSynchronously is true), the
callback is executing on the original
thread and you do not have to call
Sync(). If the operation completed
asynchronously (that is,
CompletedSynchronously is false), the
callback is executing on a thread pool
or I/O completion port thread and you
must call Sync().
http://msdn.microsoft.com/en-us/library/ee728598.aspx