In my program, there will be a time where I have to call
Thread.Sleep(Timeout.Infinite);
to "temporarily pause" the BackgroundWorker. A groupbox will be shown before the backgroundworker thread sleeps. That groupbox has a button that should "wake up" the backgroundworker thread and continue it.
If I call Thread.Interrupt() (which by the way I can't seem to use unless I create a Thread object, which I shouldn't do) at the button_Click event, like:
private void button1_Click(object sender, EventArgs e)
{
Thread.Interrupt(); //interrupt thread
}
The Thread it "would" interrupt is the UI Thread, am I right? What I need to do is to Interrupt the BackgroundWorker thread. How do I do this?
EDIT: Thanks for those that replied to this question. I'll use AutoResetEvent. Seems more appropriate for my use.
Let me start with the high-level concept:
What you should do is have a token that you check every so often in the code that is being executed by the BackgroundWorker. When the token is set your background code will stop the normal flow and just check the token every now and then and when the token is cleared the background code can continue processing.
So the interesting part above is the token. What I would do is maybe have a boolean that I check and when that boolean is set to true I would block the thread by waiting on ManualResetEvent. When you want to resume the processing you set the boolean to false and use the Set() method of the ManualResetEvent to release allow the code to continue.
You have to look at ManualResetEvent
Usage:
ManualResetEvent e = new ManualResetEvent(false); //global variable
e.WaitOne(); // Thread will wait until event is triggered
e.Set(); // Trigger event from other thread
You should use a semaphore to sync between the threads.
once you want the background worker to "sleep", grap a handle on the semaphore, once you click on the "wake up" button, release the semaphore and the background worker will resume..
From your GUI thread (the one that shows the button) your should declare
Semaphore s = new Semaphore(0, 1);
on the background worker thread - this statement initialize a semaphore with a default value of 0 (locked)
on your backgroundworker thread/code call:
s.WaitOne();
this statement actually cause the background worker to wait until the semaphore is released by the gui thread (your wake up button).
on the button click handler, call the:
s.Release();
the release operation allows the background worker code to resume running.
Related
I've got a BackgroundWorker that occasionally needs to call into the UI thread to perform some work and retrieve a result. To achieve this I'm using the following from within the background thread:
App.Current.Dispatcher.Invoke(new Action(() => { /* some code that updates local data */ });
As the app is exiting, I want to be able to tell the BackgroundWorker to quit but I want to allow it to finish any current operations. In other words, I want to call BackgroundWorkerObj.CancelAsync() and then pump messages until the thread has exited.
I've tried the following, but the Invoke() in the background thread still blocks (though the UI thread is still churning):
Worker.CancelAsync();
while (Worker.IsBusy)
{
DispatcherFrame Frame = new DispatcherFrame();
Frame.Continue = false;
Dispatcher.PushFrame(Frame);
}
What's the correct way to do this? How can the UI thread wait on the BackgroundWorker while still executing Invokes from that BackgroundWorker object?
This sort of shutdown deadlock is exactly why you shouldn't use Invoke for this purpose.
Change it to BeginInvoke(), and for communications back to the worker thread use an event.
I would use Task.Run since you're on .NET 4.0. but anyways. You have to do it the other way around. Wait for the backgroundworker to finish and then exit the application. There is no way to wait for the background thread to finish in an closing event while keeping the main thread responsive. This while loop will block the main thread and message pump until the background thread is done.
Try this
private BackgroundWorker _worker;
protected override OnFormClosing( object sender , FormClosingEventArgs e )
{
base.OnFormClosing( sender , e );
// Cancel's the closing and keeps the form alive
e.Cancel = _worker.IsBusy;
}
private void RunWorkerCompleted( object sender , RunWorkerCompletedEventArgs e)
{
// Work is done, so close the form
Close();
}
In WinForms application I start worker thread that adds data to root a XElement.
Then in main thread I need to wait while worker thread finishes it's work (to get complete XElement), and output this XElement to a textarea.
If I call .Join() on the main thread - it freezes until another thread stops (and user can't click any button on the main form).
Is it possible to unblock main thread while waiting for another thread to finish it's work??
I've tried:
1.
BeginInvoke(new Action(() => {
XmlManager.whReady.WaitOne();
xDocString = xResultDoc.ToString();
}));
2.
string xDocString = String.Empty;
new Thread(() => { xDocString = XelementToString(); }).Start();
txtXmlTree.Text = xDocString;
public string XelementToString() {
XmlManager.whReady.WaitOne();
return xResultDoc.ToString();
}
But it had no effect.
EventWaitHandle XmlManager.whReady.WaitOne(); is being .Set() in the worker thread just before it closes.
Yes, you can use async/await
string docString = await Task.Run(() => {
XmlManager.whReady.WaitOne();
return xResultDoc.ToString();
}).ConfigureAwait(true);
//Execution flow will resume here once the thread is done.
....
//Now do something here with the text (e.g. display it).
...
For example, if you want to run this on a button click, you would have (note the async modifier):
private async void button1_Click(object sender, EventArgs e)
{
...The code above goes here...
}
As to why your code is not working as expected (both of your attempts):
Your code is blocking, because it causes the action to be executed on the thread on which the main form's handle was created (so the UI thread). You typically call BeginInvoke from another (non UI) thread to tell the UI to do something.
You start a thread and then immediately expect to have xDocString ready to use. It doesn't work that way. By the time this line is executed txtXmlTree.Text = xDocString; the thread may or may not have finished executing (most likely not finished).
I hope now you see why await is a way better option. You don't have to synchronize your workers with the main thread, you don't have worry about context switching and making sure UI code executes on the UI thread.
You can use BackgroundWorker class since it's a WinForm application.
The BackgroundWorker will let the sub-task to be run in the background, and notify the main form on it's completion (as well as progress, if needed), so the main form will be able to display the output in the text area once the sub-task is complete.
When I started main thread I also started a second thread, but the second thread still waits for the main thread. I expected that when I started a new thread it would go do work without being connected to the main thread. So why does panel1 become visible after the main thread finishing its job?
private void comboBox1_SelectedIndexChanged_1(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(threadUI));
thread.Start();
// This class is loading something from the server on the main thread
excel.get_data_from_excel(comboBox1.SelectedItem.ToString(), this);
}
private void threadUI()
{
if (panel1.InvokeRequired)
{
panel1.Invoke(new newDelegate(threadUI));
}
else
{
panel1.Visible = true;
}
}
The Invoke method will not return until the main thread executes the delegate. If you want the background thread to continue without waiting for the main thread, use BeginInvoke instead.
However, be aware that only one thing can be occurring on the main thread. You can call Invoke or BeginInvoke, but the delegate won't be processed until the main thread is idle. That is, if get_data_from_excel takes a long time, your panel1.Visible=true will not take effect until get_data_from_excel completes, comboBox1_SelectedIndexChanged_1 returns, and the main thread becomes idle.
If you truly want to make these things "parallel", you must execute get_data_from_excel in a background thread.
You're doing long running non-UI work in the UI thread.
The second thread that you create is doing nothing but calling Invoke and doing a bit of work. What Invoke does is run some code in the UI thread, which is currently busy doing some non-UI work. It won't be scheduled to run until after that work finishes.
What you should do is do that long running non-UI work in another thread, rather than the UI thread.
It looks like you're confused about Invoke().
Invoke() is used to queue up a delegate for the thread that displays panel1. However, Invoke() blocks UNTIL that delegate has run to completion. Therefore, you have your second thread blocking at Invoke().
If you would like to have an action run on the main thread, while calling it from the second thread WITHOUT blocking... then use BeginInvoke(). It will queue up the delegate and then return immediately.
Servy's Comment
Servy brings up a good point. Whats the point of the second thread, if it is just going to immediately call the first? There isn't any need to create a second thread if you are just going to immediately adjust a control's property.
But it looks like you are grabbing data from excel. That section of code should be in the second thread... and then with it's output use BeginInvoke().
if i use code just like this it also waiting for the complete next
line finishing its job
private void comboBox1_SelectedIndexChanged_1(object sender, EventArgs e)
{
panel1.Visible = true;
excel.get_data_from_excel(comboBox1.SelectedItem.ToString(), this);
}
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();
I'm looking for a way to restart a thread that has been stopped by Abort()..
public partial class MyProgram : Form
{
private Thread MyThread = new Thread(MyFunction);
private System.Windows.Forms.Button startStopBtn = new System.Windows.Forms.Button();
public MyProgram()
{
MyThread.Start();
startStopBtn += new EventHandler(doStop);
startStopBtn.Text = "Stop";
}
private static void MyFunction()
{
// do something
}
private void doStop(object sender, EventArgs e)
{
MyThread.Abort();
startStopBtn -= new EventHandler(doStop);
startStopBtn += new EventHandler(doStart);
startStopBtn.Text = "Start";
}
private void doStart(object sender, EventArgs e)
{
MyThread.Start(); // << Error returned when clicking the button for 2nd time
startStopBtn -= new EventHandler(doStart);
startStopBtn += new EventHandler(doStop);
startStopBtn.Text = "Stop";
}
}
Any idea?
Once you have aborted your thread, you cannot start it again.
But your actual problem is that you are aborting your thread. You should never use Thread.Abort().
If your thread should be paused and continued several times, you should consider using other mechanisms (like AutoResetEvent, for example).
[EDIT]
The simplest solution to abort a thread, as mentioned by Ian Griffiths in the link above, is:
The approach I always recommend is dead simple. Have a volatile bool field that is visible both to your worker thread and your UI thread. If the user clicks cancel, set this flag. Meanwhile, on your worker thread, test the flag from time to time. If you see it get set, stop what you're doing.
The only thing that you need to do to make it work properly, is to rearrange your background method so that it runs in a loop - so that you can periodically check if your flag has been set by a different thread.
If you need to have pause and resume functionality for the same worker thread, instead of the simple volatile bool flag approach, you could go for a slightly more complex approach, a synchronizing construct such as AutoResetEvent. These classes also provide a way to put the worker thread to sleep for a specified (or indefinite) amount of time between signals from the non-worker thread.
This thread contains a concrete example with Start, Pause, Resume and Stop methods. Note how Brannon's example never aborts the thread. It only fires an event, and then waits until the thread finishes gracefully.
Simply add MyThread = new Thread(MyFunction) before calling MyThread.Start() in doStart(). Do not create the thread outside of your methods, the space there is thought for declarations.
Please note that killing a thread with thread.Abort() can be very dangerous, as it might cause unexpected behavior or might not correctly dispose resources owned by the thread. You should try to accomplish clean multi threading, like Groo described in his answer.
The simple answer is, you can't. Once a thread has been aborted, you can't restart it. Just create a method or something, that returns a Thread object just how you need it. When you need a new Thread, just get it from that method.
No, there isn't, but why would you want to? Just start up a new thread, with the same ThreadStart, and the same parameter (if any).
If you really need to interrupt the thread function and resume, you should set a condition and then check it periodically during processing.
That would allow you to stop processing for some amount of time and then resume.
I've used events and Wait calls to accomplish a similar task.
The easiest way is to not abort the thread.
I really don't understand why people provide information if they do not know that is correct..
How can a real programmer suspend or stop processing a thread for sometime and then release it and thereby making the code vulnerable...
#Brad-- m sorry.. but your idea was not good..
#Rhythmic - You need to work on your way to approach things..
BFree was somewhat right if you people got him the same way he wanted to say..
You just need to re-declare that..
below is the example:
Public Shared Sub ResetAbort()
Dim ThreadPleaseWait As New Thread(New ThreadStart(AddressOf YourSubName))
YourThreadName.Start()
Thread.Sleep(2000)
YourThreadName.Abort()
End Sub
Now you can use this Sub anywhere you want to start the thread. It will automatically abort the thread.
If you want to start the thread on Button1_click() event and stop it on Button2_Click() event use this:
in Button1_click() event
Dim ThreadPleaseWait As New Thread(New ThreadStart(AddressOf YourSubName))
YourThreadName.Start()
in Button2_click() event
YourThreadName.Start()
doing this way you will abort you thread where ever you want and will initialize it again.
You can also use YourThreadName.ThreadState.Running property to check if the thread is running or not(Just to avoid multiple instances of the same thread.....