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);
}
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.
I've got a GUI interface which has a start and a cancel button. After starting, the main thread which is the GUI thread, is creating a second thread which will do the actual work. When pressing the cancel button, all it does is set a boolean value which tells the working thread to stop its work and end. The problem is that the main GUI thread remain stuck even though I'm sure that the working thread has finished what it was doing. Why is that?
Here is some of the code:
private Thread workerThread;
private SomeClass fs;
private void buttonSearch_Click(object sender, EventArgs e)
{
//do some initializations
fs = new SomeClass();
workerThread = new Thread(fs.WorkMethod);
workerThread.Start();
}
private void buttonCancel_Click(object sender, EventArgs e)
{
fs.StopWork();
workerThread.Join();
}
inside SomeClass:
private bool keepWorking;
public void StopWork()
{
keepWorking= false;
}
public void WorkMethod()
{
if (keepWorking)
{
//do some stuff with recursion
}
}
does someone know why won't the main thread wake up after calling join?
I have also tried debugging to see what happens if I change the keepWorking variable to false manually and the method does reach its' end.
Your WorkMethod has a call to Invoke in there that is invoking a delegate to run on the UI thread and then block until it finishes. Since your UI thread is currently blocking on the call to Join waiting for the background thread, the UI thread is unable to call that delegate.
You now have both threads each waiting on the other, and no progress is being made. This is called a "deadlock".
Also, keepWorking should be marked as volatile as it's being accessed from multiple threads; as it stands the background thread can be accessing an outdated/cached value of that variable for quite some time after the main thread changes it. Marking it as volatile prevents the runtime from making such optimizations.
The solution here is to not block the UI thread with a call to Join. If you need to have some code execute when the background thread ends then you'll need to asynchronously fire that code when the thread finishes instead of synchronously blocking.
In my wpf application I have added a piece of code in the button click as below:
private void btn_convert_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(new ThreadStart(WorkerMethod));
t.SetApartmentState(ApartmentState.MTA);
t.IsBackground = true;
t.Start();
}
Inside my WorkerMethod() method I have some code like the line below:
btn_convert.Content = "Convert";
When it reaches to this line it throws the exception as the calling thread cannot access this object because a different thread owns it.
I dont want to use Dispatcher as it freezes the UI.. UI should be responsive so I have not opted for Dispatcher invoke or BeginInvoke.
Please give me your valuable suggestions.
I dont want to use Dispatcher as it freezes the UI.. UI should be responsive so i am not opted for Dispatcher invoke or BrginInvoke.
That just shows that you've used the dispatcher badly.
You must access the UI from the UI thread. That doesn't mean your whole WorkerMethod needs to run on the UI thread, but this line:
btn_convert.Content = "Convert";
definitely does. So you might want to keep your current code for starting a thread (do you really need to set the apartment state though) but change any code accessing the UI to use the dispatcher. For example:
Action setButtonContentAction = () => btn_convert.Content = "Convert";
Dispatcher.BeginInvoke(setButtonContentAction);
Alternatively, depending on what your WorkerThread is doing - and if you're using C# 5 - you might want to use the new async features. That can make it easier to keep UI work on the UI thread, but it does depend on what else is going on.
UI changes can only be applied by the main thread. You can check if the main thread call is necessary:
if (btn_convert.InvokeRequired)
{
btn_convert.Invoke((MethodInvoker)(() => btn_convert.Content = "Convert"));
}
When i use invoke inside AddListBoxItem function as seen below software become unreponsive and frozen but if i use BeginInvoke it works. Why is that happening ?
visual studio 2010 , C# 4.0
private void button2_Click(object sender, EventArgs e)
{
var watch = Stopwatch.StartNew();
Parallel.For(2, 20, (i) =>
{
var result = SumRootN(i);
AddListBoxItem("root " + i + " : " + result);
});
AddListBoxItem(watch.ElapsedMilliseconds.ToString());
}
private delegate void AddListBoxItemDelegate(object item);
private void AddListBoxItem(object item)
{
if (this.listBox1.InvokeRequired)
{
this.listBox1.Invoke(new AddListBoxItemDelegate(this.AddListBoxItem), item);
}
else
{
this.listBox1.Items.Add(item);
}
}
Your UI thread will wait for Parallel.For to complete before it continues. That means it can't process any further UI messages until it's completed.
Now when the worker threads call Invoke, they wait until the UI thread processes the delegate before they continue. So they're waiting for the UI thread to get free, basically.
Hence, you have a deadlock - the UI thread is waiting for the tasks, which are waiting for the UI thread... BeginInvoke works because then the task threads don't wait for the delegates to be processed in the UI thread.
I would suggest that you don't call Parallel.For in the UI thread to start with. You'll block the UI until it completes anyway, which isn't a good idea. Do the whole thing in a background thread - then you can still use Invoke if you want, and while it's doing the computation the UI will still be responsive.
It sounds like you are deadlocking the UI thread. This makes perfect sense, as your button2_Click doesn't exit until For completes, and in particular, no message-loop events can be processed until button2_Click has completed. If you are on a different thread, Invoke uses a message-loop event, and does not return until that item is processed - so nothing will ever be done - and For / button2_Click will never complete.
By using BeginInvoke you simply queue this work - BeginInvoke returns immediately. This means that the For can complete, which lets button2_Click complete, which then allows the message-loop events to be processed (updating the UI).
I think it's because auf the Mainthread beeing blocked in the above Click event, waiting to finish AddListBoxItem, which is waiting the buttion2_click event to return.
You shouldn't have Controller logic in your UI, so the main problem that the Click isn't calling back a logic in a different thread. After Implementing an thread for your logic, your GUI wouldn't block and would refresh easy in any situation.