I have spent two whole days figuring out why the threading in my WinForms application doesn't work. I really need some help here.
In my application, button1_Click event will call a method but if the method runs for too long, I want to abort it.
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
Thread t1 = new Thread(new ThreadStart(ExtractData));
t1.Start();
//Wait for 5 seconds, if t1 is not finished executing, abort the thread
autoResetEvent.WaitOne(5000);
if (autoResetEvent.WaitOne()== false)
{
t1.Abort();
}
button1.Enabled = true;
}
private void ExtractData()
{
//Get data from web service..
autoResetEvent.Set();
}
I consider button1_Click event as my main thread and ExtractData() will be in thread t1. After ExtractData() is finished doing it's work, I want autoResetEvent.Set() to wake up autoResetEvent.WaitOne() in the main thread & therefore the main thread execution can be finished. However the main thread will just stop at autoResetEvent.WaitOne() & remains in waiting state. Did I do anything wrong?
You're waiting on the event twice, and after the first time the event has been reset, as it is an auto reset event. Change this:
autoResetEvent.WaitOne(5000);
if (autoResetEvent.WaitOne()== false)
{
t1.Abort();
}
to
if (autoResetEvent.WaitOne(5000)== false)
{
t1.Abort();
}
So that you only wait on it once.
Also, as others have mentioned, your code is blocking the gui thread for the entire 5 seconds that you wait, meaning your applcation will become unresponsive. You should look into other options, such as using async/await.
Related
I'm updating a textbox from the 'Backgroundworker' with the "Invoke" method.
But when I "refresh" the picturebox in the ui , the 'backgroundworker' thread locks. So there is no problem of updating ui only. For this example, counter doesn't incrementing.Thanks.
int counter = 0;
private delegate void SafeCallDelegate(string text);
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Thread.Sleep(3000); //do stuff
}
private void btnRefresh_Click(object sender, EventArgs e)
{
pictureBox1.Refresh();
}
private void UpdateTextBox(string text)
{
if (textBox1.InvokeRequired)
{
var d = new SafeCallDelegate(UpdateTextBox);
textBox1.Invoke(d, new object[] { text });
}
else
{
textBox1.Text = text;
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
Thread.Sleep(100);
counter++;
UpdateTextBox(counter.ToString());
}
}
Control.Invoke will block until the call on the main thread has completed, and due to the 3s sleep in the paint event, this will take a while. If you do not want this, use .BeginInvoke, that will post the update to the main thread and return immediately.
However, the comments are correct in that async/await/Task.Run or a timer is better way to go.
The reason is because Invoke means to wait for the main UI thread to do the work before the background thread can continue.
Invoke is done using messages, as is painting.
When the background thread calls Invoke, a message is put on the message queue that the main UI thread is processing, and when it gets to that message, whatever the delegate you passed does will be done in the context of the main UI thread. While the message is being processed, the background thread will be waiting for Invoke to return, which will only happen after the message has been processed.
Now, what happens if you do a Thread.Sleep(3000) in the paint of the picturebox? The painting is also done using messages, and the message loop code looks simplified like this:
while (true)
{
var message = WaitForAndGetNextMessage();
ProcessMessage(message); // this will return only when message has been processed
}
So when the paint message for the picturebox arrives, and you sleep for 3 seconds inside, the message loop is not processing messages. If there is a message from the Invoke method in the queue, this will also be delayed for the same 3 seconds, and while this is delayed, your background thread is waiting for the message to be processed, which means it too will be stuck waiting for 3 seconds.
One way to "fix" this would be to use the fire-and-forget BeginInvoke instead of Invoke. This will put the message in the queue but the background thread will not wait for it to be processed and will continue immediately. This, of course, would mean that your 100ms background loop would be adding about 30 of those messages into the queue while the paintbox is being "painted" and all of those 30 messages will be processed afterwards in a short amount of time.
Would this be fixed by tasks that others have hinted at in the comments? Not if you still do something lengthy (like Thread.Sleep) in an event handler.
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive.Windows.Forms and add using System.Reactive.Linq; - then you can do this:
public Form1()
{
InitializeComponent();
Observable
.Interval(TimeSpan.FromMilliseconds(100.0))
.ObserveOn(this)
.Subscribe(n => textBox1.Text = n.ToString());
}
That's it. It's a timer that fires every 100 milliseconds, it pushes the call to the current form, and then it updates the text box.
It is a lot cleaner than background workers or plain threads.
This question already has answers here:
Control.Invoke is hanging
(2 answers)
Closed 9 years ago.
I am writing an application using multithread. The application basically has a UI and a thread doing some work in the background and updating the UI. When I close the form, In the formclosing event, I notify the worker thread to stop. However, for some reasons It blocks and I have no ideas what caused it to block. Below is the simplified code of my problem, my actual code is more complicated.
namespace CmdTest
{
public partial class Form1 : Form
{
Thread _workerThread;
static object _lock;
static bool _stopFlag;
public Form1()
{
_lock = new object();
_stopFlag = false;
_workerThread = new Thread(new ThreadStart(ThreadDoWork));
InitializeComponent();
_workerThread.Start();
}
delegate void UpdateUI();
public void UpdateUICallback()
{
//Doing stupid things
int i = 0;
while (i < 10000)
{
i++;
}
}
public void ThreadDoWork()
{
if (this.InvokeRequired)
{
UpdateUI updateUI = new UpdateUI(UpdateUICallback);
while (true)
{
//telling the UI thread to update UI.
this.Invoke(updateUI);
lock (_lock)
{
if (_stopFlag)
return;
}
}
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//tell the worker thread to terminate.
lock (_lock)
{
_stopFlag = true;
Monitor.Pulse(_lock);
}
while (!_workerThread.Join(0))
{
}
}
}
}
The problem is if I use
lock (_lock)
{
_stopFlag = true;
Monitor.Pulse(_lock);
}
to stop the worker thread in a button event, the worker thread will stop but not in the form closing event. Any help would be appreciated. Thanks.
Your FormClosing event is waiting for the background thread to close before it lets the method end. Note that it will be running in the UI thread.
Your background thread is, in a loop, invoking a method in the UI thread and, since you use Invoke rather than BeginInvoke, it is waiting for that method to complete before continuing.
The UI is running the closing event, sitting there doing nothing. Since it's doing nothing, it can't process any of the other events in the message loop, including the one method that the background thread is waiting on.
Both threads are each waiting on each other, and no productive work is being done. This is the definition of a deadlock. It will sit that way forever.
Note that this is a race condition though; if you're lucky enough for the form to be closed after a given Invoke call completes and before the flag is next checked (which is hard; it spends very little time between those operations) then your program won't deadlock.
As for how to fix it; that's hard to say. The whole example is somewhat contrived.
Perhaps you don't need to be invoking to the UI at all from the background worker; if you're not actually doing UI work, you probably shouldn't be doing this.
Do you really need to wait for the background worker to finish in your closing handler? It's possible that you do, but often you wouldn't.
I know there is Thread.Sleep and System.Windows.Forms.Timer and Monitor.Wait in C# and Windows Forms. I just can't seem to be able to figure out how to wait for X seconds and then do something else - without locking the thread.
I have a form with a button. On button click a timer shall start and wait for 5 seconds. After these 5 seconds some other control on the form is colored green. When using Thread.Sleep, the whole application would become unresponsive for 5 seconds - so how do I just "do something after 5 seconds"?
(transcribed from Ben as comment)
just use System.Windows.Forms.Timer. Set the timer for 5 seconds, and handle the Tick event. When the event fires, do the thing.
...and disable the timer (IsEnabled=false) before doing your work in oder to suppress a second.
The Tick event may be executed on another thread that cannot modify your gui, you can catch this:
private System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
private void StartAsyncTimedWork()
{
myTimer.Interval = 5000;
myTimer.Tick += new EventHandler(myTimer_Tick);
myTimer.Start();
}
private void myTimer_Tick(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
/* Not on UI thread, reenter there... */
this.BeginInvoke(new EventHandler(myTimer_Tick), sender, e);
}
else
{
lock (myTimer)
{
/* only work when this is no reentry while we are already working */
if (this.myTimer.Enabled)
{
this.myTimer.Stop();
this.doMyDelayedWork();
this.myTimer.Start(); /* optionally restart for periodic work */
}
}
}
}
Just for completeness: with async/await, one can delay execute something very easy (one shot, never repeat the invocation):
private async Task delayedWork()
{
await Task.Delay(5000);
this.doMyDelayedWork();
}
//This could be a button click event handler or the like */
private void StartAsyncTimedWork()
{
Task ignoredAwaitableResult = this.delayedWork();
}
For more, see "async and await" in MSDN.
more completeness:
Depending on your Framework, there is a good chance you will have DispatcherTimer class that can handle the invocation internally (WPF-variants). (finde details in ms docs)
Have you tried
public static Task Delay(
int millisecondsDelay
)
You can use like this:
await Task.Delay(5000);
reference: https://msdn.microsoft.com/en-us/library/hh194873(v=vs.110).aspx
You can start an asynchronous task that performs your action:
Task.Factory.StartNew(()=>
{
Thread.Sleep(5000);
form.Invoke(new Action(()=>DoSomething()));
});
[EDIT]
To pass the interval in you simply have to store it in a variable:
int interval = 5000;
Task.Factory.StartNew(()=>
{
Thread.Sleep(interval);
form.Invoke(new Action(()=>DoSomething()));
});
[/EDIT]
You can wait UI thread the way you want it to work.
Task.Factory.StartNew(async() =>
{
await Task.Delay(2000);
// it only works in WPF
Application.Current.Dispatcher.Invoke(() =>
{
// Do something on the UI thread.
});
});
if you're using .Net Framework 4.5 or higher version, you can use Task.Run instead of Task.Factory.StartNew just like below.
int millisecondsDelay = 2000;
Task.Run(async() =>
{
await Task.Delay(millisecondsDelay);
// it only works in WPF
Application.Current.Dispatcher.Invoke(() =>
{
// Do something on the UI thread.
});
});
You are looking at it wrong.
Click the button, it kicks off a timer with an interval of x seconds. When those are up it's eventhandler executes the task.
So what don't you want to happen.
While the x seconds are elapsing.?
While The task is executing?
If for instance it's you don't want the button to be clicked until delay and task are done. Disable it in the button click handler, and enable it on task completion.
If all you want is a five second delay prior to the task, then you should pass the start delay to the task and let it take care of it.
your application hangs because you are invoking the 5 second sleep/wait on the main UI thread. put the sleep/wait/whatever action in a separate thread (actually System.Windows.Forms.Timer should do that for you) and when it completes invoke the action that turns some control green. remember to check InvokeRequired. here's a short sample (SetText can be called from another thread, if it is the call will instead be invoked on the main UI thread where the textbox is on):
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
I took the sample from here (well worth a read!).
#eFloh in the post marked as answer said:
The Tick event may be executed on another thread that cannot modify
your gui, you can catch this ...
That is not what the docs say.
You are using a System.Windows.Forms.Timer in your example code.
That is a Forms.Timer.
According to the C# docs the Timer events are raised on the UI thread.
This Windows timer is designed for a single-threaded environment where
UI threads are used to perform processing. It requires that the user
code have a UI message pump available and always operate from the same
thread ...
Also see stackoverflow post here
I have this code to pause and resume a thread:
public partial class frmMain : Form
{
(...)
ManualResetEvent wait_handle = new ManualResetEvent(true);
(...)
}
private void frmMain_Shown(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(TheLoop));
}
private void TheLoop(object stateinfo)
{
bool hasInfo = true;
while (doLoop)
{
wait_handle.WaitOne();
bool hasLines = GetInfo();
if (hasLines)
{
//Consuming time Operation 1
System.Threading.Thread.Sleep(7000);
if (CurrentLine < line.Count - 1)
CurrentLine++;
else
{
bool hasInfo2 = GetInfo2();
if (hasInfo2)
{
//Consuming time Operation 2
System.Threading.Thread.Sleep(7000);
}
CurrentLine = 0;
}
}
else
System.Threading.Thread.Sleep(40000); //Wait to query again
}
}
private void btnPauseResume_Click(object sender, EventArgs e)
{
if (btnPauseResume.Text == "Pause")
{
btnPauseResume.Text = "Resume";
wait_handle.Reset();
}
else
{
btnPauseResume.Text = "Pause";
wait_handle.Set();
}
}
The code above shows a cycle information, it works find to pause and resume the "first consuming time operation" but doesn't work for the second one, if I press the button to pause the thread in the second consuming time operation, this one continues and when the first one appears again, then it pauses there.
What am I missing here?
Thx
Have you considered using a Background Worker instead since you are using WinForms? It would probably be easier than trying to 'Pause' a thread. You can check the CancellationPending property to see if a user has elected to cancel the operation. The link has a good sample to look at.
I have never seen someone pausing a thread. Create a delegate and event inside the class or method that you are executing on a separate threat. Execute that event whenever you wish to pause your thred.
There is not any reason that I can see that would prevent a second call to WaitOne from working if placed before the 2nd time consuming operation. Since you are using a ManualResetEvent the wait handle's state will persist until either Set or Reset is called. That means if you resume the thread by calling Set then both calls to WaitOne will pass through. Likewise, if you pause the thread by calling Reset then both calls to WaitOne will block. Of course, it will not be possible to predict where the worker thread will pause if there is more than one call to WaitOne.
Got it guys! the thing is where you put the WaitOne(). For instance, if I have a While Loop (like my example) if I put the wait before it, no matter how many times I hit the pause button, it won't stop the thread, it's logic since the loop already began, but if I put it at the end, then it will work.
Appreciated your help.
When my C# application closes it sometimes gets caught in the cleanup routine. Specifically, a background worker is not closing. This is basically how I am attempting to close it:
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
backgroundWorker1.CancelAsync();
while (backgroundWorker1.IsBusy) ; // Gets stuck here.
}
Is there a different way that I should be doing this? I am using Microsoft Visual C# 2008 Express Edition. Thanks.
ADDITIONAL INFORMATION:
The background worker does not appear to be exiting. This is what I have:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!backgroundWorker1.CancellationPending)
{
// Do something.
}
}
I've also modified the cleanup code:
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
while (backgroundWorker1.IsBusy)
{
backgroundWorker1.CancelAsync();
System.Threading.Thread.Sleep(1000);
}
}
Is there something else that I should be doing?
Some pretty good suggestions, but I don't believe they address the underlying issue: canceling a background task.
Unfortunately, when using BackgroundWorker, termination of your task depends on the task itself. The only way your while loop will terminate, is if your background task checks its Cancel property and returns or breaks from its current process.
Example Base
For example, consider
private readonly BackgroundWorker worker = new BackgroundWorker ();
public void SomeFormEventForStartingBackgroundTask ()
{
worker.DoWork += BackgroundTask_HotelCalifornia;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync ();
}
// semantically, you want to perform this task for lifetime of
// application, you may even expect that calling CancelAsync
// will out and out abort this method - that is incorrect.
// CancelAsync will only set DoWorkEventArgs.Cancel property
// to true
private void BackgroundTask_HotelCalifornia (object sender, DoWorkEventArgs e)
{
for ( ; ;)
{
// because we never inspect e.Cancel, we can never leave!
}
}
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
worker.CancelAsync();
// [politely] wait until background task terminates
while (worker.IsBusy);
}
This is what is happening by default. Now, maybe your task isn't an infinite loop, perhaps it is just a long-running task. Either way, your main thread will block [actually it is spinning, but whatevs] until the task completes, or doesn't as the case may be.
If you have personally written and can modify the task, then you have a few options.
Example Improvement
For instance, this is a better implementation of the above example
private readonly BackgroundWorker worker = new BackgroundWorker ();
// this is used to signal our main Gui thread that background
// task has completed
private readonly AutoResetEvent isWorkerStopped =
new AutoResentEvent (false);
public void SomeFormEventForStartingBackgroundTask ()
{
worker.DoWork += BackgroundTask_HotelCalifornia;
worker.RunWorkerCompleted += BackgroundTask_Completed;
worker.WorkerSupportsCancellation = true;
worker.RunWorkerAsync ();
}
private void BackgroundTask_HotelCalifornia (object sender, DoWorkEventArgs e)
{
// execute until canceled
for ( ; !e.Cancel;)
{
// keep in mind, this task will *block* main
// thread until cancel flag is checked again,
// so if you are, say crunching SETI numbers
// here for instance, you could still be blocking
// a long time. but long time is better than
// forever ;)
}
}
private void BackgroundTask_Completed (
object sender,
RunWorkerCompletedEventArgs e)
{
// ok, our task has stopped, set signal to 'signaled' state
// we are complete!
isStopped.Set ();
}
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
worker.CancelAsync();
// [politely] wait until background task terminates
isStopped.WaitOne ();
}
While this is better, it's not as good as it could be. If you can be [reasonably] assured your background task will end, this may be "good enough".
However, what we [typically] want, is something like this
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
worker.CancelAsync();
// [politely] wait until background task terminates
TimeSpan gracePeriod = TimeSpan.FromMilliseconds(100);
bool isStoppedGracefully = isStopped.WaitOne (gracePeriod);
if (!isStoppedGracefully)
{
// KILL! KILL! KILL!
}
}
Alas, we cannot. BackgroundWorker does not expose any means of forceful termination. This is because it is an abstraction built on top of some hidden thread management system, one which could potentially destabalize other parts of your application if it were forcefully terminated.
The only means [that I have seen at least] to implement the above is to manage your own threading.
Example Ideal
So, for instance
private Thread worker = null;
// this time, 'Thread' provides all synchronization
// constructs required for main thread to synchronize
// with background task. however, in the interest of
// giving background task a chance to terminate gracefully
// we supply it with this cancel signal
private readonly AutoResetEvent isCanceled = new AutoResentEvent (false);
public void SomeFormEventForStartingBackgroundTask ()
{
worker = new Thread (BackgroundTask_HotelCalifornia);
worker.IsBackground = true;
worker.Name = "Some Background Task"; // always handy to name things!
worker.Start ();
}
private void BackgroundTask_HotelCalifornia ()
{
// inspect cancel signal, no wait period
//
// NOTE: so cheating here a bit, this is an instance variable
// but could as easily be supplied via parameterized thread
// start delegate
for ( ; !isCanceled.WaitOne (0);)
{
}
}
private void App_FormClosing(object sender, FormClosingEventArgs e)
{
// [politely] request termination
isCanceled.Set ();
// [politely] wait until background task terminates
TimeSpan gracePeriod = TimeSpan.FromMilliseconds(100);
bool isStoppedGracefully = worker.Join (gracePeriod);
if (!isStoppedGracefully)
{
// wipe them out, all of them.
worker.Abort ();
}
}
And that there, is a decent introduction on thread management.
Which is best suited for you? Depends on your application. It is probably best not to rock the boat, and modify your current implementation to ensure that
your background task inspects and respects the Cancel property
your main thread waits for completion, as opposed to polling
It is very important to compare and evaluate the pros and cons of each approach.
If you must control and guarantee termination of someone else's tasks, then writing a thread management system that incorporates the above may be the way to go. However you would lose out on out-of-box features like thread pooling, progress reporting, cross-thread data marshalling [worker does that, no?], and a bunch of other stuff. Not to mention, "rolling your own" is often error prone.
Anyway, hope this helps :)
Kevin Gale is correct in stating that your BackgroundWorker's DoWork handler needs to poll for CancellationPending and return if a cancellation is requested.
That being said, if this is happening when your application is shutting down, you can just ignore it safely, as well. BackgroundWorker uses a ThreadPool thread, which is, by definition, a background thread. Leaving this running will not prevent your application from terminating, and the thread will automatically be torn down when your application shuts down.
In the background worker thread you need to check the BackgroundWorker.CancellationPending flag and exit if it is true.
The CancelAsync() just sets this flag.
Or to put it another way. CancelAsync() doesn't actually cancel anything. It won't abort the thread or cause it to exit. If the worker thread is in a loop and checks the CancellationPending flag periodically it can catch the cancel request and exit.
MSDN has an example here although it doesn't use a loop in the worker routine.
This code is guaranteed to deadlock when the BGW is still running. BGW cannot complete until its RunWorkerCompleted event finished running. RunWorkerCompleted cannot run until the UI thread goes idle and runs the message loop. But the UI thread isn't idle, it is stuck in the while loop.
If you want the BGW thread to complete cleanly, you have to keep your form alive. Check this thread to see how to do that.
Try:
if (this.backgroundWorker1.IsBusy) this.backgroundWorker1.CancelAsync();