I have a web application that, based on input from a user, calculates a combination of products to fit the requirements and returns a quote for the user. This calculation can take some time, so I am displaying a progress dialog to the user.
The progress dialog is a UserControl with an UpdatePanel, that contains some text labels, a button, and a timer which is used for the AsynchPostBackTrigger trigger. This is declared on my aspx page but is hidden by default.
Before, I created a separate thread to do the heavy work while updating the progress dialog using the main thread. There were no issues there. here is where I start the thread:
protected void StartQuoteCalculation(object sender, EventArgs e)
{
TotalPanelCount = CurrentQuote.PanelCount;
ProgressDialog.ShowPopup();
CalculateQuoteThread = new Thread(new ThreadStart(CalculateQuote));
CalculateQuoteThread.Start();
}
Where CalculateQuote does the heavy work.
However, I've recently had to change something unrelated to this exactly, but it is now affecting the calculation of the combinations.
A variable that's used during the quote calculation is now stored in the user Session. The session is lost when we start the worker thread above so when accessing this variable it was throwing a NullReference exception. To fix this, I've tried passing the HttpContext.Current to the new thread like so:
protected void StartQuoteCalculation(object sender, EventArgs e)
{
TotalPanelCount = CurrentQuote.PanelCount;
ProgressDialog.ShowPopup();
HttpContext ctx = HttpContext.Current;
CalculateQuoteThread = new Thread(new ThreadStart(() =>
{
HttpContext.Current = ctx;
CalculateQuote();
}));
CalculateQuoteThread.Start();
}
However, the session seems to stay valid for a short while, but after a few seconds it is still throwing NullReferenceException. HttpContext.Current is valid, but the HttpContext.Current.Session is null.
So, I have a few questions:
If it's possible to pass the HttpContext to the other thread, why is
the Session going to null after short time? How can I prevent this?
Is it possible to create, display and update the ProgressDialog panel in the new thread? (to keep the product calculation in the main thread with valid Session data)
I've tried a couple things on #2, but as I am not so knowledgable on asp.net and web development in general, I haven't been successful.
Rather than storing individual variables in session I would store the entire object which contains the state of that calculation. That way as the website is polling for status updates the application can retrieve the current state by retrieving that object from session. It's going to be "in progress," "complete" (the output), or an exception (never happens!)
If you want users to be able to run multiple processes then you could store a Dictionary<guid, YourProcess> and then the client could receive a guid and use it to request the status.
This way the client has a reference to the object which is executing that process, but the thread executing that process doesn't need a reference to the Session or HttpContext. It only needs to know which instance of that class it's operating on, and update it with either progress or the final result.
Related
Sorry for the lengthy post, I just want to illustrate my situation as best as possible. Read the items in bold and check the code if you want the quick gist of the issue.
I use ClickOnce to deploy a C# application, and have opted to have my application check for updates manually using the ApplicationDeployment Class rather than letting it do the update checking for me.
The program is a specialized network scanner that searches for network devices made by the company I work for. Once the main window is loaded, a prompt is displayed asking if the user would like to scan the network. If they say Yes, a scan begins which can take a minute or two to complete depending on their network settings; otherwise it just waits for the user to do some action.
One of the last things I do in Form_Load is create a new thread that checks for updates. This had all been working fine for several months through about 12 releases and has suddenly stopped working. I didn't change the update code at all, nor change the sequence of what happens when the app starts.
In staring at the code, I think I see why it is not working correctly and wanted to confirm if what I think is correct. If it is, it begs the question as to why it DID work before - but I'm not too concerned with that either.
Consider the following code:
frmMain.cs
private void Form1_Load(object sender, EventArgs e)
{
// set up ui, load settings etc
Thread t = new Thread(new ParameterizedThreadStart(StartUpdateThread));
t.Start(this);
}
private void StartUpdateThread(object param)
{
IWin32Window owner = param as IWin32Window;
frmAppUpdater.CheckForUpdate(owner);
}
frmAppUpdater.cs
public static void CheckForUpdate(IWin32Window owner)
{
if (ApplicationDeployment.IsNetworkDeployed) {
Console.WriteLine("Going to check for application updates.");
parentWindow = owner;
ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
ad.CheckForUpdateCompleted += new CheckForUpdateCompletedEventHandler(ad_CheckForUpdateCompleted);
ad.CheckForUpdateProgressChanged += new DeploymentProgressChangedEventHandler(ad_CheckForUpdateProgressChanged);
ad.CheckForUpdateAsync();
// CAN/WILL THE THREAD CREATED IN FORM1_LOAD BE TERMINATED HERE???
}
}
When the CheckForUpdateAsync() callback completes, if no update is available the method call simply returns; if an update IS available, I use a loop to block until 2 things occur: The user has dismissed the "Would you like to scan prompt" AND no scan is currently running.
The loop looks like this, which takes place in ad_CheckForUpdateCompleted:
while (AppGlobals.ScanInProgress || AppGlobals.ScanPromptVisible) {
System.Threading.Thread.Sleep(5000);
}
I sleep for 5 seconds because I figured this was happening in a separate thread and it has seemed to work well for a while.
My main question about the above code is:
When ad.CheckForUpdateAsync(); is called from CheckForUpdate does the thread I created in Form1_Load terminate (or might it terminate)? I suspect it may because the subsequent Async call causes the method to return, and then start another thread?
The only reason I am confused is because this method WAS working for so long without hanging the application and now all of the sudden it hangs and my best effort at debugging revealed that it was that Sleep call blocking the app.
I'd be happy to post the full code for frmAppUpdater.cs if it would be helpful.
When ad.CheckForUpdateAsync(); is called from CheckForUpdate does
the thread I created in Form1_Load terminate (or might it terminate)?
If the CheckForUpdateAsync() call is asynchronous then yes, the thread will terminate, no it won't otherwise.
If you suspect the Sleep to have caused the application hang then these two variables AppGlobals.ScanInProgress and AppGlobals.ScanPromptVisible are probably always set to true! You should start looking at the code that is setting them to true and see what is going on there.
In order to avoid an application hang, you could introduce a variable to avoid sleeping indefinitely:
int nTrials = 0;
while ((AppGlobals.ScanInProgress || AppGlobals.ScanPromptVisible) && (nTrials < 5)) {
System.Threading.Thread.Sleep(5000);
nTrials++;
}
// Check the results and act accordingly
I personally do not like using Sleep for thread synchronization. .NET offers a bunch of classes that are perfect for thread synchronization, WaitHandle being one of them.
See this post at Asynchronous Delegates Vs Thread/ThreadPool?
your form load method seems to be doing synchronous work. you mention that you are using clickonce deployment. Has the binary location changed after the previous release or has permissions on this resource changed. Looks like the work (checkupdates) in the Thread is never finishing and is never handed back to the form.
as an immediate fix, I would change the Thread approach to Delegate - if you use delegate, then this becomes less of a customer issue (the form will respond to end user) but the underlying problem remains.
as the next step, i would go through http://msdn.microsoft.com/en-us/library/ms229001.aspx and do the troubleshoot
I am writing a GUI app for monitoring and I'd like to get some advice on it's logic. Basically all the app needs to do is connect to a distant server every x minutes, check if something was changed, get changes if any, act upon them (update local db and so on, depending on changes).
My first idea was:
Have a checkbox (monitoring on/off). On click (if checked) starts a Timer.
Timer launches a BackgroundWorker in it's Tick method.
DoWork method does the connecting / retrieving info stuff
a) on WorkDone handler method gets the info from background worker and does local updates with it
b) on WorkDone handler method triggers one or more of custom events "SomethingChanged" depending on changes it got; EventListeners handle local updates from there.
My main problem is calling Worker from Timer since I added Worker to the Form and now they are on different threads (is that correct description?) and then passing results around is a similar problem. I was reading about delegates but still not sure what to use when and how, and if it's really necessary in the first place. Do I need both bgWorker and Timer? Do I need custom events or can I just do all work inside workDone with Switch(result)? Is this general principle good in the first place, maybe there's something better and I am reinventing the wheel? Thank you in advance!
From an architecture point of view:
Message Queues decouple bits of your application. You can in Windows Forms applications rely on the Message Queue that Windows creates and manages for you. Google for PostMessage/GetMessage etc. This is generally called "message passing".
Typical Arcitecture
One part of your app "pushes" a request into a queue
Some other part of your app "pulls" a request from a queue and writes a result to a second queue.
The first part can then "pull" requests from the second "results" queue and display to a user.
So it looks like this:
App --> REQUESTS QUEUE --> processing engine --> RESULTS QUEUE --> App
The processing engine could be in the same app, on the same thread or in a different thread/process (or even different machine).
You can use simple queues : say a Queue<string>() (as long as you use locks to access it) or increase complexity or more and more complex/functional queues.
Issues with the naive strategy and other solutions ... things to think about:
What happens if you make a new request while the old one has not yet completed?
What happens if an error occurs? Do you want errors inline? You can use another queue for errors?
Do you want retries?
What happens if a message is lost? (i.e. a request was pushed, but no response comes in...)? There are transactional queues etc.
Sample Code
object oLock = new object();
Queue<string> requests = new Queue<string>();
Queue<string> responses = new Queue<string>();
Thread mThread;
AutoResetEvent mEvent = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
mThread = new Thread(ProcessingEngine);
mThread.IsBackground = true;
mThread.Start();
}
private void ProcessingEngine()
{
string result;
string request = null;
while (true)
{
try
{
mEvent.WaitOne();
lock (oLock)
{
request = requests.Dequeue();
}
var wc = new WebClient();
result = wc.DownloadString(request);
lock (oLock)
{
responses.Enqueue(result);
}
}
catch (Exception ex)
{
lock (oLock)
{
responses.Enqueue(ex.ToString());
}
}
}
}
private void timer1_Tick(object sender, EventArgs e)
{
lock (oLock)
{
//Stick in a new request
requests.Enqueue("http://yahoo.com");
//Allow thread to start work
mEvent.Set();
//Check if a response has arrived
if (responses.Any())
{
var result = responses.Dequeue();
listBox1.Items.Add(result.Substring(1,200));
}
}
}
}
If you use System.Windows.Forms.Timer instead of System.Threading.Timer, your Tick handler will be called from Form's message loop and you'll have full access to all controls - it will be safe to call bgWorker.RunWorkerAsync(). As for retrieving results - RunWorkerCompleted is also called from message loop thread and you can safetly update your UI here.
The solution is simple - INVOKE back into the main thread. THere is an Invoke method on the winform control. This will basically change threads for execution to the UI thread, and allow you to manipulate the UI.
Do that "block" (i.e. not once per control but once when you have news).
Can anybody suggest any reasons why a C# timer (created in a code-behind class) would stop without being told to?
My timer starts on page load and then stops when I click a button. I don't need to click the button for it to sometimes stop. IIS is not being restarted to my knowledge and no errors are being thrown either.
This is confusing me quite a bit...
Thanks.
// This gets called on page_load
private void checkTimer()
{
if (!parentTimer.Enabled) // If parent timer is not enabled then it is probably the start of a new day (after a night time server backup which kills timers)
{
parentTimer.Interval = 60000; // One minute
parentTimer.Elapsed += new ElapsedEventHandler(parentTimer_Elapsed); // Define what happens when elapsed
parentTimer.AutoReset = true; // Set timer to repeat
parentTimer.Enabled = true; // Start the timer
}
}
protected void btnCancel_Click(object sender, System.EventArgs e)
{
parentTimer.Stop();
...etc...
}
Note: I do not change ParentTimer at all in its elapsed method.
Basically ParentTimer governs a list of ChildTimers. If ParentTimer elapses it checks if one or more of the ChildTimers have elapsed too, if so, there is an event, if not then it resets the ChildTimer and carries on.
My suspicion is it's because the worker process for the page is stopping at the end of the request.
You could try increasing the request time out, but a better question is Can you explain why you're trying to do this ? What is the problem you're trying to solve ?
Remember, that regardless of all the fluff that ASP.Net puts around your code to make you feel comfortable (session state, viewstate etc), a web request is stateless and should be considered as a distinct pass of logic, it's not like a windows application where a background thread of code in your void Main(...) function is constantly running.
A timer is tied to the thread that created it and in the case of ASP.net the thread that handles each page request issued by a given user will change frequently due to the use of worker threads and the thread pool.
Using a timer at page-level simply won't work; you need to be tracking the state at Session-level (tied to the particular user) as your starting point.
In fact, I just wouldn't use timers at all in a web application, because their execution is simply not guaranteed.
If you're using this to run a background task - consider firing up your own worker thread in Application_Start or something like that. The thread will be terminated when the app pool recycles. You should also look at manually shutting the thread down the application is being shut down too.
Be careful with this, however, this thread can't assume it's always the only one running - due to IIS overlapped recycling, when a new one fires up the old one could still be running in the old AppDomain.
protected System.Threading.Thread m_searchthread = null;
Question: I've implemented a search algorithm, which a user can start from an ASP.NET website.
When the user clicks on Button "Start", this code is executed:
m_searchthread = new System.Threading.Thread(new System.Threading.ThreadStart(Search));
m_searchthread.IsBackground = true;
m_searchthread.Start();
Now I want to make it possible to abort the search thread.
So the user presses on Button "Abort", and this code is executed:
m_searchthread.Abort();
My problem now is: the postback of the abort button resets m_searchthread to NULL...
I am aware of the fact that the thread should be aborted using a flag. The core problem is just: how to stop a thread when you loose all the variables ?
Any variables you want persisted between postbacks need to go into either static variables, view/session state, the ASP.NET cache, or some other form of backing store.
When you postback, you will then need to go and fetch this from whatever backing store you chose.
I'm not attempting to make any comments on how you're performing your async task.
What is happening is your thread variable is local to the page. When the postback occurs, the page life cycle completes and all references to the variable are lost. Each postback triggers a new page life cycle.
You will encounter this problem whenever you have some state residing on the server that you need to remember between postbacks. ViewState is how ASP.NET controls remember their content between postbacks without you needing to repopulate them each time.
The direct problem here is that you try to use a member variable (field) to store the Thread.
If you store it like Session["MyThreadKey"] = myThread; you at least can get your thread back in the next Postback. Like myThread = Session["MyThreadKey"];
And then you can start to think about a way to avoid Thread.Abort.
You will probably want to wrap the thread in an object that also contains the stop-flag. (and then store that object in Session).
As commented, this will only work on a single-server solution. The better approach all around would be to define a (WCF) service to do the searching.
Why not store the flag in the Session or ViewState and do the check in the page load event.
Don't start/stop jobs from a page because is dangerous and you have no control on tasks running and started.
I suggest you to use Quartz.NET. There is a useful answer about using it in Asp.NET:
How to use Quartz.net with ASP.NET
In order to stop the thread based on a flag, you have to implement in the following manner:
Define the flag itself in a static class or a module where it is accessible to the thread:
bool StopTheThread = false;
Inside your thread, check for this flag somewhere inside your loop so as to get out of the thread gracefully!
void SearchProc()
{
bool exitCond = False;
while (!exitCond)
{
//some execution
//some execution
//some execution
//check for the thread-abort flag:
if (StopTheThread)
{
System.Threading.Thread.Suspend(); //this will suspend the current thread
}
}
}
Ok what id like to do is to load a page that displays 90% of content, and load the last 10% asynchronously.
As im rendering the page i programatically create an update panel which i pass to a thread. When this thread finishes it then updates the updatepanel on the main thread.
public void test(object parameter)
{
Thread.Sleep(2000);
var updpanel = (UpdatePanel)parameter;
updpanel.ContentTemplateContainer.Controls.Add(new LiteralControl("HI"));
updpanel.Update();
}
protected void Page_Load(object sender, EventArgs e)
{
var th = new Thread(new ParameterizedThreadStart(test));
var updpanel = new UpdatePanel() { UpdateMode = UpdatePanelUpdateMode.Conditional };
ContentPlaceHolder1.Controls.Add(updpanel);
th.Start(updpanel);
}
Failing this, in a single threaded approach, do i just keep polling the page to see if it has finished or not?
One thing to keep in mind is, even though ASP.Net development seems like it's closely related to windows development, there's a big difference: your code to handle a request, most of the time, is executed in a fraction of a second and done... In this example, once the main thread has completed, the generated page has already been sent to the requestor, leaving your secondary thread running in the background of your server, working on a page that has already been sent out.
What you'll probably need to do is generate the 90% of the page, and then send it out. On your page, you will need to use ajax to, on page load (for the client), request the other 10% from the server... You'll probably want to do the javascript with a library like jquery, and on the server end setup a web service to handle the requests.
You cannot use threading this way on the server. Your second thread will probably not complete until after the page have been processed and the request lifecycle is complete.
Depending on what you are trying to achieve: do you need to process data in parallel server-side you should look into Asynchronous Pages in ASP.Net 2.0.
Another approach would be to render the page ("90%" as you call it) and use ajax on the client to request additional data.