i'm in trouble. In my web app i have a code that creates a thread on the Click event of a button to perform a data intensive task, the is like this:
protected void button_Click(object sender, EventArgs e)
{
// Show loader image
loader.Show();
// Creating the thread
System.Threading.ParameterizedThreadStart ts = new System.Threading.ParameterizedThreadStart(RunThread);
Thread t = new Thread(ts);
t.Name = "BackgroundThread";
t.Priority = ThreadPriority.AboveNormal;
t.Start(HttpContext.Current);
}
private void RunThread(object state)
{
// Setting the current thread property as the background thread
CurrentThread = Thread.CurrentThread;
if (IsThreadRunning(CurrentThread))
{
CurrentThread.Join(TimeSpan.FromMinutes(6d));
}
// DO SOME HEAVY STUFF
}
In the button click i show the loader. The problem is: Even calling Join in the BackgroundThread, the Page_Load event gets called frequently making the page refresh. In other words, while the RunThread doesn´t finish, the Page_Load is getting called. Could i prevent this from happening?
OBS: What i wanna do is: show the loader while the data intensive thread is running without make repetitive refreshs on the page.
The only way to check long running task status from web page without polling (making postback from time to time) is to use Web Sockets, but i'm pretty sure using it would be big overhead for this kind of tasks.
I would use ajax requests to prevent full page reload.
Seems like you need to read more about the whole web request and response process.
Related
I have a windows form (C#.NET) with a statusLabel that I can not seem to get to update in the middle of a process in event handler methods. My code looks like this...
void Process_Completed(object sender, EventArgs e)
{
string t = "Process is finished!";
this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t });
}
void Process_Started(object sender, EventArgs e)
{
string t = "Process has begun";
this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t });
}
private delegate void StatusLabelUpdator(string text);
private void updateStatusLabel(string text)
{
StatusLabel1.Text = text;
statusStrip1.Invalidate();
statusStrip1.Refresh();
statusStrip1.Update();
}
When I run the code, once the process starts, the Process_Started method is triggered, and a couple seconds later the Process_Completed method is triggered. For some reason I can not get the status label to ever display "Process has begun". It only ever displays "Process is finished!". As you can see I have tried invalidating, refreshing and updating the status strip which contains the status label but no success. I can't call update/refresh/invalidate on the statuslabel itself because those methods are not available to it. What am I doing wrong?
ADDED INFO:
The "process" is started by a button click on the form which calls a method in a separate class that looks like this:
public void DoSomeProcess()
{
TriggerProcessStarted();
System.Threading.Thread.Sleep(2000); // For testing..
TriggerProcessComplete();
}
and inside the TriggerProcessxxxx methods I trigger the events using this code...
var EventListeners = EH.GetInvocationList(); //EH is the appropriate EventHandler
if (EventListeners != null)
{
for (int index = 0; index < EventListeners.Count(); index++)
{
var methodToInvoke = (EventHandler)EventListeners[index];
methodToInvoke.BeginInvoke(this, EventArgs.Empty, EndAsyncEvent, new object[] { });
}
}
Finally, I have added Application.DoEvents() to the updateStatusLabel method but it did not help. I am still getting the same result. Here is my update method.
private void updateStatusLabel(string text)
{
StatusLabel1.Text = text;
statusStrip1.Refresh();
Application.DoEvents();
}
So I guess the "processing" is taking place on the UI thread but eventhandler is invoked on it's own thread which then invokes the control update back on the UI thread. Is this a dumb way of doing things? Note: The class that contains the DoSomeProcess() method is in a separate .NET ClassLibrary that i am referencing.
If you're doing your processing on the UI thread, it won't be able to do anything else (like redraw updated labels) while the processing is running. So for instance, if the processing is happening because the user clicked a button and is triggered by the button click handler (without explicitly placing it on another thread), it's running on the UI thread. Even though you update the label's text, it doesn't get drawn until it receives a paint message, at which point it's probably busy doing your processing.
The answer is to do long-running processing on a separate thread. The hack (IMHO) is to use Application.DoEvents to let the UI thread do some UI stuff during your processing. If you put one of those after updating the label and before you start your processing, odds are pretty high the label will get repainted. But then, during the processing, no further paint events can get processed (leading to half-drawn windows when someone moves another app window over your app and back, etc.). Hence my calling it a hack (even though, er, um, I've been known to do it :-) ).
Edit Update based on your edits:
Re
So I guess the "processing" is taking place on the UI thread but eventhandler is invoked on it's own thread...
I'm assuming DoSomeProcess is triggered from the UI thread (e.g., in direct response to a button click or similar). If so, then yes, your processing is definitely on the UI thread. Because TriggerProcessStarted triggers your callback asynchronously via BeginInvoke, you have no idea when it will run, but in any case your code then immediately launches into processing, never yielding, so no one else is going to be able to grab that thread. Since that's the UI thread, the call to the delegate will block on the Invoke call setting the label's text, whereupon it has to wait for the UI thread (which is busy processing). (And that's assuming it's scheduled on a different thread; I couldn't 100% convince myself either way, because Microsoft has two different BeginInvokes -- which IIRC one of the designers has acknowledged was a Really Dumb Idea -- and it's been a while since I fought with this stuff.)
If you make the TriggerProcessStarted calls to your callbacks synchronous, you should be okay. But ideally, schedule the processing (if it's not doing UI) on its own thread instead.
I have some code that works as follows:
App.xaml calls a SetUp() method which populates a local collection using async calls and exposes the collection as a public property.
That's all good.
Now I make an instance of the first page in my Silverlight app like so
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new Summary();
}
In the constructor of Summary() I expect those async calls to have been complete and my collection to be filled but the async calls have not yet complete. Even if I do a Thread.Sleep(100000....) before i make an instance on Summary() this is the case
And the thing is that until the constructor of Summary() is exited and the UI displayed to the user my async calls do not get kicked off!
What!!!
Is there anything I can do about that or is that just the way asyn calls work i.e. they wait until the current until of work finished before firing?
This is how I work round this situation (I'll use simple string download as an example):-
private void Application_Startup(object sender, StartupEventArgs e)
{
WebClient web = new WebClient();
web.DownloadStringCompleted += (s, args) =>
{
// Do stuff with args.Result);
this.RootVisual = new Summary();
};
web.DownloadStringAsync(new Uri("SomeUrl.txt", UriKind.Relative));
}
Note that the construction of Summary and the assignment to RootVisual are defered until the asynchronous download is complete.
Silverlight was specifically designed to not have any blocking operations -- that's why you have to do async in the first place. What you really have to do is make your app run properly while waiting for the async completion handler to execute.
This is by design. The alternative would be that the user has to wait longer before he sees anything at all.
If you really want to prevent showing an incomplete summery then first shown another page with 'waiting' or a progress bar. Fire the async events from that page or its parent. Then show the Summary when the async call returns.
To deal with an incomplete Summary you might want to use ICommand
I know this is an old thread, but for all following reader i want to provide my experience with a similar problem. If i understood you correctly - the reason why your async calls do not complete is because you block the main thread. I ran into the same problem, if you block the main thread none of the other threads continue. The solution was to do the async calls inside a backgroundworker and show, like pauldendulk said, a waiting - page. Use Debug.WriteLine() to monitor the process in the output.
I have an ASP.NET page that requires data from another DLL and the process might take a long time. So, I set out to use APM. But when I try that, the page just never stops loading. It loads indefinitely. Is there something I'm doing wrong?
Here is my web page:
List<string> allVoices;
GetAllVoicesDelegate getVoicesDelegate;
internal delegate List<String> GetAllVoicesDelegate();
protected void Page_Load(object sender, EventArgs e)
{
Page.AddOnPreRenderCompleteAsync(new BeginEventHandler(BeginGetDropDownValues),
new EndEventHandler(EndGetDropDownValues));
}
public IASyncResult BeginGetDropDownValues(object o,EventArgs args,AsyncCallback cb,object obj)
{
getVoicesDelegate = MyLib.getStrings;
return getVoicesDelegate.BeginInvoke(EndGetDropDownValues,null);
}
public void EndGetDropDownValues(IASyncResult ar)
{
allVoices = getVoicesDelegate.EndInvoke(ar);
}
protected override OnPreRenderComplete(EventArgs e)
{
if(allVoices.Count>0)
{
foreach(String str in allVoices)
{
Response.Write(str);
}
}
base.OnPreRenderComplete(e);
}
Here is the MyLib.getStrings() method in another DLL:
public List<String> getStrings()
{
List<String> allStr=new List<string>();
allStr.Add("1");
allStr.Add("2");
allStr.Add("3");
allStr.Add("4");
}
If you have to get data from a long running process, making async calls from the web page won't help you because in the end, the process needs to finish before the page can finish rendering. Making async calls frees you to do other stuff in the mean time, but the page can't render until all the activity on it is finished.
I think you'll have to take a different approach, either using Ajax to poll the server until the response is ready, or creating an intermediate page that tells the user to wait until the process is complete. Once it's complete, the page refreshes and the user sees the data.
I'm not overly familiar with your approach to asynchronous computing in asp.net (I generally use ajax to make requests to web services, etc.)
One possible flaw, from my point of view is the OnPreRenderComplete handler. This handler is not asynchronous to the best of my knowledge. It is used to handle the event of when all page controls are loading and ready.
The OnPreRenderComplete method is called when the prerendering stage of the page life cycle is complete. At this stage of the page life cycle, all controls are created and the page is ready to render the output.
In this event handler, you are making reference to allVoices class-level member which is processed by the async handlers. Is it possible then the code in this handler has to wait for the async delegate to complete? (in essence defeating the purpose of async).
Again just a guess. What happens if you take out the `OnPreRenderComplete' handler? or comment out the code within?
Additionally - I think the article you referenced is a little outdated and may not apply to web application development? Generally asynchronous programming on the web is done via AJAX and/or Web services/WCF?
Most likely your problem is your ASP.NET page compiling (typically takes seconds), and not the dll loading (typically takes only a few milliseconds).
The code sample you have provided launches some work on a separate thread and allows the asp.net worker process to continue processing your page request without blocking on your long running task. If the long running task takes a long time to complete, then your page will take a long time to render.
Start by replacing the begin with some kind of mocked up code to return an invoice. If your page completes almost immediately, then your async wiring is correct and you probably need to move to some kind of polling approach which uses the first request to start the task and renders a "we are processing your request" page to the user. This page then uses ajax to poll the system for some kind of completion flag, once that is received, the user is redirected to the result page and the results are displayed.
I have a windows form (C#.NET) with a statusLabel that I can not seem to get to update in the middle of a process in event handler methods. My code looks like this...
void Process_Completed(object sender, EventArgs e)
{
string t = "Process is finished!";
this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t });
}
void Process_Started(object sender, EventArgs e)
{
string t = "Process has begun";
this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t });
}
private delegate void StatusLabelUpdator(string text);
private void updateStatusLabel(string text)
{
StatusLabel1.Text = text;
statusStrip1.Invalidate();
statusStrip1.Refresh();
statusStrip1.Update();
}
When I run the code, once the process starts, the Process_Started method is triggered, and a couple seconds later the Process_Completed method is triggered. For some reason I can not get the status label to ever display "Process has begun". It only ever displays "Process is finished!". As you can see I have tried invalidating, refreshing and updating the status strip which contains the status label but no success. I can't call update/refresh/invalidate on the statuslabel itself because those methods are not available to it. What am I doing wrong?
ADDED INFO:
The "process" is started by a button click on the form which calls a method in a separate class that looks like this:
public void DoSomeProcess()
{
TriggerProcessStarted();
System.Threading.Thread.Sleep(2000); // For testing..
TriggerProcessComplete();
}
and inside the TriggerProcessxxxx methods I trigger the events using this code...
var EventListeners = EH.GetInvocationList(); //EH is the appropriate EventHandler
if (EventListeners != null)
{
for (int index = 0; index < EventListeners.Count(); index++)
{
var methodToInvoke = (EventHandler)EventListeners[index];
methodToInvoke.BeginInvoke(this, EventArgs.Empty, EndAsyncEvent, new object[] { });
}
}
Finally, I have added Application.DoEvents() to the updateStatusLabel method but it did not help. I am still getting the same result. Here is my update method.
private void updateStatusLabel(string text)
{
StatusLabel1.Text = text;
statusStrip1.Refresh();
Application.DoEvents();
}
So I guess the "processing" is taking place on the UI thread but eventhandler is invoked on it's own thread which then invokes the control update back on the UI thread. Is this a dumb way of doing things? Note: The class that contains the DoSomeProcess() method is in a separate .NET ClassLibrary that i am referencing.
If you're doing your processing on the UI thread, it won't be able to do anything else (like redraw updated labels) while the processing is running. So for instance, if the processing is happening because the user clicked a button and is triggered by the button click handler (without explicitly placing it on another thread), it's running on the UI thread. Even though you update the label's text, it doesn't get drawn until it receives a paint message, at which point it's probably busy doing your processing.
The answer is to do long-running processing on a separate thread. The hack (IMHO) is to use Application.DoEvents to let the UI thread do some UI stuff during your processing. If you put one of those after updating the label and before you start your processing, odds are pretty high the label will get repainted. But then, during the processing, no further paint events can get processed (leading to half-drawn windows when someone moves another app window over your app and back, etc.). Hence my calling it a hack (even though, er, um, I've been known to do it :-) ).
Edit Update based on your edits:
Re
So I guess the "processing" is taking place on the UI thread but eventhandler is invoked on it's own thread...
I'm assuming DoSomeProcess is triggered from the UI thread (e.g., in direct response to a button click or similar). If so, then yes, your processing is definitely on the UI thread. Because TriggerProcessStarted triggers your callback asynchronously via BeginInvoke, you have no idea when it will run, but in any case your code then immediately launches into processing, never yielding, so no one else is going to be able to grab that thread. Since that's the UI thread, the call to the delegate will block on the Invoke call setting the label's text, whereupon it has to wait for the UI thread (which is busy processing). (And that's assuming it's scheduled on a different thread; I couldn't 100% convince myself either way, because Microsoft has two different BeginInvokes -- which IIRC one of the designers has acknowledged was a Really Dumb Idea -- and it's been a while since I fought with this stuff.)
If you make the TriggerProcessStarted calls to your callbacks synchronous, you should be okay. But ideally, schedule the processing (if it's not doing UI) on its own thread instead.
I have an asynchronous operation that for various reasons needs to be triggered using an HTTP call to an ASP.NET web page. When my page is requested, it should start this operation and immediately return an acknowledgment to the client.
This method is also exposed via a WCF web service, and it works perfectly.
On my first attempt, an exception was thrown, telling me:
Asynchronous operations are not allowed in this context.
Page starting an asynchronous operation has to have the Async
attribute set to true and an asynchronous operation can only be
started on a page prior to PreRenderComplete event.
So of course I added the Async="true" parameter to the #Page directive. Now, I'm not getting an error, but the page is blocking until the Asynchronous operation completes.
How do I get a true fire-and-forget page working?
Edit: Some code for more info. It's a bit more complicated than this, but I've tried to get the general idea in there.
public partial class SendMessagePage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string message = Request.QueryString["Message"];
string clientId = Request.QueryString["ClientId"];
AsyncMessageSender sender = new AsyncMessageSender(clientId, message);
sender.Start();
Response.Write("Success");
}
}
The AsyncMessageSender class:
public class AsyncMessageSender
{
private BackgroundWorker backgroundWorker;
private string client;
private string msg;
public AsyncMessageSender(string clientId, string message)
{
this.client = clientId;
this.msg = message;
// setup background thread to listen
backgroundThread = new BackgroundWorker();
backgroundThread.WorkerSupportsCancellation = true;
backgroundThread.DoWork += new DoWorkEventHandler(backgroundThread_DoWork);
}
public void Start()
{
backgroundThread.RunWorkerAsync();
}
...
// after that it's pretty predictable
}
If you are running webforms set Ansync = "true" in your .aspx page where you are making the request. <%# Page Language="C#" Async="true" ... %>
If you don't care about returning anything to the user, you can just fire up either a separate thread, or for a quick and dirty approach, use a delegate and invoke it asynchrnously. If you don't care about notifying the user when the async task finishes, you can ignore the callback. Try putting a breakpoint at the end of the SomeVeryLongAction() method, and you'll see that it finishes running after the page has already been served up:
private delegate void DoStuff(); //delegate for the action
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
//create the delegate
DoStuff myAction = new DoStuff(SomeVeryLongAction);
//invoke it asynchrnously, control passes to next statement
myAction.BeginInvoke(null, null);
Button1.Text = DateTime.Now.ToString();
}
private void SomeVeryLongAction()
{
for (int i = 0; i < 100; i++)
{
//simulation of some VERY long job
System.Threading.Thread.Sleep(100);
}
}
OK, here's the problem: the Async attribute is for the case where your page is going to call some long-running task that also blocks the thread, and then your page needs the output from that task in order to return info to the user. For example, if your page needed to call a web service, wait for its response, and then use the data from the response to render your page.
The reason you'd use the Async attribute is to avoid blocking the thread. This is important because ASP.NET applications use a thread pool to serve requests, and there are only a relatively small number of threads available. And if each call ties up the thread while waiting on the web service call, then soon you're going to hit enough concurrent users that users are going to have to wait until these web service calls complete. The Async attribute lets the thread return to the thread pool and serve other concurrent visitors to your web site, rather than forcing it to sit still doing nothing while waiting for the web service call to return.
The upshot for you is this: the Async attribute is designed for the case where you can't render the page until the asynchronous task completes, and that's why it doesn't render the page immediately.
You need to launch your own thread, and make it a daemon thread. I don't remember the exact syntax for that, but you can easily find it in the doc by searching the BCL doc for "daemon". This means the thread will keep your application from shutting down while it is alive, which is important because ASP.NET and IIS reserve the right to "recycle your process" when they deem it necessary, and if that happens while your thread is working, your task will be stopped. Making the thread daemon will prevent this (except for some possible rare edge cases ... you'll find out more when you find the documentation on this).
That daemon thread is where you will kick off these tasks. And after you've told the daemon thread to do the task, you can immediately render your page ... so the rendering of the page will happen immediately.
Even better than a daemon thread in your ASP.NET process, though, would be to implement a Windows Service for doing the task. Have your ASP.NET application communicate the task to be performed to the Service. No need for a daemon thread and no need to worry about your ASP.NET process being recycled. How do you tell the Service to do the task? Perhaps through WCF, or perhaps by inserting a record into a database table that the Service polls. Or a number of other ways.
EDIT: Here's another idea, which I have used before for this very same purpose. Write the info about your task into an MSMQ queue. Have another process (maybe even on another machine) pull from that queue and do the time-consuming task. The job of inserting into a Queue is optimized to return as quickly as possible, so your thread won't block while the data you put in the Queue is sent across the wire or anything like that. It is one of the fastest ways to make note of the fact that a task needs to be done without waiting for that task to execute.
You can work around this limitation quite easily and without even setting Async to true.
public void Start()
{
new Task(() =>
{
backgroundThread.RunWorkerAsync();
}).Start();
}
If you get this error when calling web service asynchronously, make sure adding the Async='true' attribute as instructed by the
exception message?
top of the page < Page Language='VB' Async='true' AutoEventWireup='false'
CodeFile='mynewpage.aspx.vb' Inherits='mynewpage' %>