So here is my problem with my Windows Phone application.
I have Page 1 that can navigate to Page 2. In Page 2 OnNavigateTo I make a async call.
This seems ok the first time I run the application, the async call creates a new worker thread and does work for me which is cool. But I realize that if I go back to Page 1 and re-invoke Page 2 the problem appears: Now I have a new worker thread from the async call while the old one was not terminated. So there is a race between the two worker threads and cause a problem to my application. I don't have direct control to the threads since they are implicitly created by async methods.
So in this case, anyone has suggestion on how to deal with it or is there a common pattern of dealing with this issue?
It depends on how you're issuing the async request. If you're using say, WebClient to do something like DownloadStringAsync you'll see that your WebClient instance has a method CancelAsync that will set the cancelled property in your Completed event handler to true. Just call CancelAsync when you leave your page and test for this in your handler and you should be good.
// in some button handler or whereever
webClient.DownloadStringAsync("http://url.com")
void OnLeavePage(object sender, EventArgs e) {
webClient.CancelAsync();
}
void OnCompleted(object sender, DownloadStringCompletedEventArgs e) {
if (e.Cancelled) {
return;
}
// do your handling
}
If you don't have CancelAsync you can pass in a UserState object that has a Cancelled property to emulate the behaviour (set it to true when you leave and test in your handler).
Related
I am using C#,.Net4.0 and Visual Studio 2010 and I am trying to get this behaviour from my windows form application when the user click on a button:
A GUI LED starts to blink
A long rung operation starts
When the operation at point 2 ends the LED stops blinking
private void Btn_WebService_Click(object sender, EventArgs e)
{
Thread_Blink = new Thread(() => { LED_Blink(LED.WS); });
Thread_Blink.Start();
// Do something that takes time.. Let's imulate with a sleep
Thread.Sleep(2000);
Thread_Blink.Abort();
}
I also tried using 3 different events and/or timers..
private void Btn_WebService_MouseDown(object sender, MouseEventArgs e)
{
Timer_Blink.Enabled = true;
}
private void Btn_WebService_Click(object sender, EventArgs e)
{
// Do something that takes time.. Let's imulate with a sleep
Thread.Sleep(2000);
}
private void Btn_WebService_MouseUp(object sender, MouseEventArgs e)
{
Timer_Blink.Enabled = false;
}
The result is always the same: The LED starts to blink only AT THE END of the long running operation (Thread.Sleep(2000);) and suddenly STOPS so that you can't see anything. Why does this happen and how can I get the desired behaviour?
I add further infos. I tried to use BackgroundWorked and implemented the wanted behavour in this way:
private void Btn_WebService_Click(object sender, EventArgs e)
{
BlinkWorker.RunWorkerAsync(LED.WS);
LedOn(LED.WS);
TestWebService(); // This method takes about 2 seconds to answer..
LedOff(LED.WS);
BlinkWorker.CancelAsync();
}
While the TestWebService() is running I get the LED off(LedOn(LED.WS);). When the TestWebService()has finished the LED comes on.
The BlinkWorker still does not work if started and cancelled inside the Click event.
FYI: If I make a button that starts the blinking and another button that stops it, it works perfectly.
The problem is, you're tying up the UI thread inside that Thread.Sleep. The UI thread is special - it's the only one that can make changes to the UI - so whilst it's busy sleeping, it's not able to service any attempts from your BackgroundWorker or LED_Blink timer callback to change the UI state.
So, you need to not tie up the UI thread. You need to put the Thread.Sleep code (or it's actual real equivalent) into the BackgroundWorkers DoWork handler, or use other means to avoid blocking the UI thread.
Common approaches today would be to make use of async/await if the real work you're trying to do already offers an asynchronous alternative (e.g. await Task.Delay(2000); would be the equivalent of your current Thread.Sleep(2000);). Unfortunately, using async and await would require you to move to a later version of .NET/Visual Studio - but you ought to be considering that anyway. 2010 is quite dated (and also, IMO, probably the worst one to stop on - it was notoriously slow), and .NET 4.0 (as opposed to .NET 4.5.2 or later) is no longer supported.
I would propose that you take a look at the BackgroundWorker class and the ReportProgress method.
I have inherited a legacy app and there is some code that can be simplified down to:
protected void SomeButton_Click(object sender, EventArgs e)
{
var otherThread = new System.Threading.Thread(delegate()
{
System.Threading.Thread.Sleep(20000);
FireAnAsyncProcess();
});
otherThread.Start();
thankYouPanel.Visible = true;
otherPanel.Visible = false;
}
The FireAnAsyncProcess() seems to be intermittently failing to run. This could of course be a number of things but I wondered:
Given the pause of 20000 above, will FireAnAsyncProcess() always be called even after the button click has been handled? Does the new thread get called regardless of what happens to the thread that called it?
Edit: the result/outcome of FireAnAsyncProcess() is not required on the web page and the page is not intending to wait for them. Rather it is trying to start the thread and assume it gets fired.
This is web environment - each request is handled in their own thread context, and the Thread.Start() isn't useful: the child thread will be aborted after the request been handled, and the parent thread aborted.
So if you need to run some piece of work on Button_Click, you should host a WCF service or Windows Service or something being able to accept some message from web-application and run the job in background.
i have a search thread in my project .
the thread is created in 'Form1()' function:
objSearchThread = new Thread(this.Thread_Func);
when user clicks the 'search' button, Start() function is called:
private void Button_Search_Click(object sender, EventArgs e)
{
objSearchThread.Start();
}
second clicked of this button, crashed! because the thread is 'Started' state.
if i change my button clicked code, and i add 'new' command. it works without error or crashing:
private void Button_Search_Click(object sender, EventArgs e)
{
objSearchThread = new Thread(this.Thread_Func);
objSearchThread.Start();
}
doesn't it need to delete thread object(objSearchThread )?
does it need to call Abort() or other functions, when thread working ends?
is second code that i write here correct?
No, you don't need to do anything. The thread will just finish when it has no more work to do. You might want to consider scheduling it to execute on the thread pool however, instead of creating a new thread each time. You could do that directly, or via the Task Parallel Libray (TPL) with the Task API. Alternatively, you might want to use BackgroundWorker, as that makes it easier to report progress to the UI. (Depending on what you're doing, you may not even need another thread at all - if you're calling a web service for example, you may be able to use the async facilities in C# 5 to make the code simpler and more efficient in one go...)
If you're using Thread directly, however, you do need to create a new Thread object each time you want to start it, as you can't reuse a thread.
Additionally, unless you need this to be an instance variable, you should consider making it a local variable. When do you ever access the thread other than in method?
I have a task that runs periodically 10 second. I do some picturebox refreshing processes by reading database. What i want is to invoke or awaken the thread and do the refresh operation when i click a button immidiately. In short, i want the refresh task to be driven by not only time but also event together. Is this possible? If yes, how? The code block for the task is shown below.
while (true)
{
// do some refresh operation
Thread.Sleep(10000);
}
void button1_Click(object sender, EventArgs e)
{
// invoke or awaken thread
}
First off I'd advise you to drop the Thread + Sleep + Invoke combo for timed operations. It's very ugly. There are timer classes for both WinForms and WPF to do these three things automatically (update the GUI periodically from the dispatcher thread). Check out System.Windows.Forms.Timer and System.Windows.Threading.DispatcherTimer.
Now for your specific question, you could simply define a common method for updating the GUI with what you need and call it both from the timer code and from a button handler.
Create an AutoResetEvent:
protected AutoResetEvent _threadCycle;
_threadCycle = new AutoResetEvent(false);
when you want to wait do:
_threadCycle.WaitOne(delay, false);
and when you want to set the event, effectually letting the thread to continue:
_threadCycle.Set();
BONUS:
when you do _threadCycle.WaitOne(delay, false); you will get a return value, true or false, that you can check to see if the timeout did expire or you are continuing because of the manually set event.
BTW:
that will ONLY work if you are doing your task in an alternate thread. If you use main thread, you will get stuck with waiting for the timeout completion anyway. Maybe it will be the best to use #Tudors answer, and get this option only as 'through the thorns' way.
You should use a AutoResetEvent for this.
What you do is something like (assuming your AutoResetEvent is called 'signal'):
while (true)
{
signal.WaitOne(10000);
...
}
And in your button handler, just do:
signal.Set();
Whenever i am updating UI in windows form using delegate it gives me cross thread exception
why it is happening like this?
is there new thread started for each delegate call ?
void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//this call delegate to display data
clsConnect(statusMsg);
}
protected void displayResponse(string resp)
{
//here cross thread exception occur if directly set to lblMsgResp.Text="Test";
if (lblMsgResp.InvokeRequired)
{
lblMsgResp.Invoke(new MethodInvoker(delegate { lblMsgResp.Text = resp; }));
}
}
The DataReceived event is always raised on a threadpool thread. You cannot update any UI control, you have to use Control.BeginInvoke(). There is no point testing InvokeRequired, it is always true.
A couple of things to keep in mind here:
Don't call Control.BeginInvoke for every single character or byte that you receive. That will bring the UI thread to its knees. Buffer the data you get from the serial port until you've got a complete response. Using SerialPort.ReadLine() usually works well, a lot of devices send strings that are terminated by a line feed (SerialPort.NewLine).
Shutting down your program can be difficult. You have to make sure to keep the form alive until the serial port stops sending. Getting an event after the form is closed will generate an ObjectDisposed exception. Use the FormClosing event to close the serial port and start a one second timer. Only really close the form when the timer expires.
Avoid using Control.Invoke instead of BeginInvoke. It can deadlock your program when you call SerialPort.Close().
Lots of ways to get in trouble. Consider using your own thread instead using DataReceived to avoid them.
Port_DataReceived is obviously an async event handler that is being raised by a thread on port monitoring component.
is there new thread started for each
delegate call ?
No, probably not. Your port monitoring component is running the poll on a background thread and the event is being raised from that thread, every time.
The point is that it is being called on a thread other than the UI, so you will need to use Control.Invoke and the patterns associated with it.
Consider this, (and read the post that may illuminate things for you)
void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//this call delegate to display data
UpdateTheUI(statusMsg);
}
private void UpdateTheUI(string statusMsg)
{
if (lblMsgResp.InvokeRequired)
{
lblMsgResp.BeginInvoke(new MethodInvoker(UpdateTheUI,statusMsg));
}
else
{
clsConnect(statusMsg);
}
}
With all of that said, I would be remiss if I didn't point out that the indirection is troubling.
Cross Thread exception happens when some none UI thread changes UI elements. Since UI elements should be changed only in the UI thread this exception is thrown. To help you understand why this happen you will have to publish you code.
Cross thread exception happens when some none UI thread changes the UI elements. To fix this use the Invoke method on the control itself. As an extra you can check InvokeRequired on the control before calling the Invoke method
See msdn