I have a c# .NET winforms app making this async call:
simpleDelegate.BeginInvoke(null, null);
My function is being called by the delegate and that all works great. The problem is, after the function finishes on the worker thread, I need the main thread to update some controls on my winform. If the worker thread tries to update these controls, .NET freaks out. But I need the main thread to remain responsive to user actions, and then call my function UpdateFormAfterServerCall() ONLY AFTER the worker thread finishes calling the async function.
I would greatly appreciate if you can give me a concise code sample, rather than abstractly explain how to do this. I've read a hundred explanations already, and am just having trouble wiring it together correctly.
Note: Before the BeginInvoke I have:
simpleDelegate = new MethodInvoker(CallServer);
From different thread if you want to update GUI which is owned by another thread use MethodInvoker
if(control.InvokeRequired)
control.Invoke( (MethodInvoker) ( ()=> updating_function() ) );
else
updating_function();
You could use a BackgroundWorker:
BackgroundWorker bw = new BackgroundWorker();
string result = null;
bw.DoWork += (s, e) =>
{
// Executes on background thread.
// UI remains responsive to user activity during this time.
result = CallServer();
};
bw.RunWorkerCompleted += (s, e) =>
{
// Executes on UI thread upon completion.
resultTextBox.Text = result;
};
bw.RunWorkerAsync();
The Control class (Form is a Control as well) has an Invoke method, you can call this from any thread to execute code on the GUI thread.
In addition, Control has a convenient InvokeRequired property that informs you whether you are on the GUI thread already. You could for instance create the following method in your form:
public class MyForm
{
// ...
public void UpdateMe()
{
if (InvokeRequired)
{
Invoke(new Action(UpdateMe));
return;
}
// Code to update the control, guaranteed to be on the GUI thread
}
}
Here is the code sample [what you want exactly] -
http://www.yoda.arachsys.com/csharp/threads/winforms.shtml
& you can read about all flavours of async here -
http://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.100).aspx
Related
What I am trying to achieve is to add text after every operation to a RichTextBox.
The problem is, that these operations take some time and instead of viewing the appended text after every operation finishes, I view them all at the end of the routine.
Semi-Pseudo code:
RichTextBox richTextBox = new RichTextBox()
if (Operation1())
{
richTextBox.AppendText("Operation1 finished");
if (Operation2())
{
richTextBox.AppendText("Operation2 finished");
if (Operation3())
{
richTextBox.AppendText("Operation3 finished");
}
}
}
The problem is that I view the appended text of operation 1 & 2 after the operation 3 is finished.
I read somewhere that I need to use something called BackgroundWorker???
Using BackgroundWorker, you would just put the background work into DoWork, and the update into RunWorkerCompleted:
var bw1 = new BackgroundWorker();
var bw2 = new BackgroundWorker();
var bw3 = new BackgroundWorker();
bw1.DoWork += (sender, args) => args.Result = Operation1();
bw2.DoWork += (sender, args) => args.Result = Operation2();
bw3.DoWork += (sender, args) => args.Result = Operation2();
bw1.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation1 ended\n");
bw2.RunWorkerAsync();
}
};
bw2.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation2 ended\n");
bw3.RunWorkerAsync();
}
};
bw3.RunWorkerCompleted += (sender, args) => {
if ((bool)args.Result)
{
richTextBox.AppendText("Operation3 ended\n");
}
};
bw1.RunWorkerAsync();
You'll notice that this runs afoul of "DRY". You could always consider abstracting the tasks for each step using something like:
var operations = new Func<bool>[] { Operation1, Operation2, Operation3, };
var workers = new BackgroundWorker[operations.Length];
for (int i = 0; i < operations.Length; i++)
{
int locali = i; // avoid modified closure
var bw = new BackgroundWorker();
bw.DoWork += (sender, args) => args.Result = operations[locali]();
bw.RunWorkerCompleted += (sender, args) =>
{
txt.Text = string.Format("Operation{0} ended\n", locali+1);
if (locali < operations.Length - 1)
workers[locali + 1].RunWorkerAsync();
};
workers[locali] = bw;
}
workers[0].RunWorkerAsync();
You could do the above 3 times, or use ReportProgress to run all tasks in one background thread, and periodically report progress.
The way that WPF (and most other UI frameworks work) is that there is a UI thread, which handles all the UI events (such as button clicking) and UI drawing.
The UI can't draw things if it's busy doing other things. What's happening is this:
You click a button
The UI thread gets a button click message, and invokes your click handler function
Now, the UI can't redraw or perform any other updates until your click handler function finishes.
Your Operation1 function finishes, and you append to the RichTextBox
The UI can't update because it's still stuck running your code
Your Operation2 function finishes, and you append to the RichTextBox
The UI can't update because it's still stuck running your code
Your Operation3 function finishes, and you append to the RichTextBox
Your function finishes, and now the UI thread is free, and it can finally process the updates and redraw itself.
This is why you see a pause and then all 3 updates together.
What you need to do is make the code that takes a long time run on a different thread so that the UI thread can remain free to redraw and update when you'd like it to. This sample program works for me - it requires .NET 4.5 to compile and run
using System.Threading.Tasks;
...
// note we need to declare the method async as well
public async void Button1_Click(object sender, EventArgs args)
{
if (await Task.Run(new Func<bool>(Operation1)))
{
richTextBox.AppendText("Operation1 finished");
if (await Task.Run(new Func<bool>(Operation2)))
{
richTextBox.AppendText("Operation2 finished");
if (await Task.Run(new Func<bool>(Operation3)))
{
richTextBox.AppendText("Operation3 finished");
}
}
}
}
What happens here is that we use the C# magical async feature, and the order of operations goes like this:
You click a button
The UI thread gets a button click message, and invokes your click handler function
Instead of calling Operation1 directly, we pass it to Task.Run. This helper function will run your Operation1 method on a thread pool thread.
We use the magic await keyword to wait for the thread pool to finish executing operation1. What this does behind the scenes is something morally equivalent to this:
suspend the current function - and thus free up the UI thread to re-draw itself
resume when the thing we're waiting for completes
Because we're running the long operations in the thread pool now, the UI thread can draw it's updates when it wants to, and you'll see the messages get added as you'd expect.
There are some potential drawbacks to this though:
Because your Operation1 method is Not running on the UI thread, if it needs to access any UI related data (for example, if it wants to read some text from a textbox, etc), it can no longer do this. You have to do all the UI stuff first, and pass it as a parameter to the Operation1 method
It's generally not a good idea to put things that take a long time (more than say 100ms) into the thread pool, as the thread pool can be used for other things (like network operations, etc) and often needs to have some free capacity for this. If your app is just a simple GUI app though, this is unlikely to affect you.
If it is a problem for you, you can use the await Task.Factory.StartNew<bool>(_ => Operation1(), null, TaskCreationOptions.LongRunning))) instead and each task will run in it's own thread and not use the thread pool any more. It's a bit uglier though :-)
Working in Winforms with C#, I have a main form that creates a second UI form on a separate thread.
viewThread = new Thread( ( ) =>
{
views = new Views.Views( displayRotators, screenLocationLeft, screenLocationTop, screenHeight, screenWidth );
Application.Run( views );
} );
viewThread.SetApartmentState( ApartmentState.STA );
viewThread.Start( );
On the second UI form, I now need to invoke a method to start a display loop that will run until I terminate the thread. I've spent about three hours trying to find a way to do this, but all I can find are examples of calling methods on the main UI thread from the worker thread, not calling methods on the worker thread FROM the main UI thread. Can anyone tell me how I can do this?
Thanks.
There are very few cases where running multiple UI threads really makes sense. Unless you have a very compelling reason to do that, I would go back to a single UI thread.
Assuming you must have multiple UI threads, the golden rule is that Controls may only be updated from the UI thread they were created on.. If you want to call methods in general, you do not have to marshal them at all. However, if those methods in turn interact with a UI control, you would just marshal that call exactly the same way you would if the call were from any other thread that is not the UI thread belonging to the control.
If you might have something like
btnOnForm1OnUIThread1_Click(...)
{
UpdateForm2OnUIThread2();
}
UpdateForm2OnUIThread2()
{
if (control.InvokeRequired)
{
// Syntax of this line may be slightly off as I'm writing from memory ...
// I normally use an extension method
control.Invoke(UpdateForm2OnUIThread2);
}
else
{
control.Text = "Blah";
}
}
a. there is nothing wrong with using multiple UI threads if you know what you do.
b. it is incorrect and confusing calling a UI thread worker - UI thread #2 is same as #1
from the developer standspoint. once you realize it - many problem will clear themselves.
c. if you want to call view2.SomeMethod() [run in UI thread #2] from view1 [run in UI thread #1] - call BeginInvoke on the view2.
The code below is simple to adopt. button1 - launches the second UI thread. button2 - calls method from UI thread 1 on the form which is in UI thread 2
Form2 form;
private void button1_Click(object sender, EventArgs e)
{
Thread viewThread = new Thread(() =>
{
form = new Form2();
Application.Run(form);
});
viewThread.SetApartmentState(ApartmentState.STA);
viewThread.Start();
label1.Text = Thread.CurrentThread.ManagedThreadId.ToString();
}
private void button2_Click(object sender, EventArgs e)
{
if (form != null)
{
form.BeginInvoke(new Action(() =>
{form.Method("Form1 is calling...");}));
}
}
in the form 2 :
public void Method(string s)
{
label1.Text = string.Format("{0} '{1}'",
Thread.CurrentThread.ManagedThreadId, s);
}
you will see that the thread number is different and yet the string has crossed them OK.
I'm trying to use a Background Worker in a WPF application. The heavy lifting task uses WebClient to download some HTML and parse some info out of it. Ideally I want to do that downloading and parsing without locking the UI and placing the results in the UI once it's done working.
And it works fine, however, if I quickly submit the "download and parse" command, I get the error:
This BackgroundWorker is currently busy and cannot run multiple tasks
concurrently
So I did some Googling and it seems that I can enable the .WorkerSupportsCancellation property of the background worker and just .CancelAsync(). However, this doesn't work as expected (canceling the current download and parse).
I still get the above error.
Here's my code:
//In window constructor.
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
//Declared at class level variable.
BackgroundWorker _backgroundWorker = new BackgroundWorker();
//This is the method I call from my UI.
private void LoadHtmlAndParse(string foobar)
{
//Cancel whatever it is you're doing!
_backgroundWorker.CancelAsync();
//And start doing this immediately!
_backgroundWorker.RunWorkerAsync(foobar);
}
POCOClassFoo foo = new POCOClassFoo();
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//This automagically sets the UI to the data.
Foo.DataContext = foo;
}
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//DOING THE HEAVY LIFTING HERE!
foo = parseanddownloadresult()!
}
Calling CancelAsync will still fire the RunWorkerCompleted event. In this event, you need to make sure that CancelAsync has not been called, by checking e.Cancelled. Until this event fires, you cannot call RunWorkerAsync.
Alternatively, I would recommend you do what Tigran suggested and create a new BackgroundWorker each time.
Further more, I would recommend storing the results of_backgroundWorker_DoWork in e.Result, then retrieve them from the same in _backgroundWorker_RunWorkerCompleted
Maybe something like this
BackgroundWorker _backgroundWorker;
private BackgroundWorker CreateBackgroundWorker()
{
var bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.DoWork += _backgroundWorker_DoWork;
bw.RunWorkerCompleted += new _backgroundWorker_RunWorkerCompleted;
return bw.
}
private void LoadHtmlAndParse(string foobar)
{
//Cancel whatever it is you're doing!
if (_backgroundWorer != null)
{
_backgroundWorker.CancelAsync();
}
_backgroundWorker = CreateBackgroundWorker();
//And start doing this immediately!
_backgroundWorker.RunWorkerAsync(foobar);
}
//you no longer need this because the value is being stored in e.Result
//POCOClassFoo foo = new POCOClassFoo();
private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
//Error handling goes here.
}
else
{
if (e.Cancelled)
{
//handle cancels here.
}
{
//This automagically sets the UI to the data.
Foo.DataContext = (POCOClassFoo)e.Result;
}
}
private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//DOING THE HEAVY LIFTING HERE!
e.Result = parseanddownloadresult()!
}
The thing is that CancelAsync() does what it climes: cancel in async way. That means that it will not stop immediately, but after some time. That time can never be calculated or predicted, so you have a couple of options:
Wait until this backround worker stops really, by waiting in cycle until IsBusy property of it becomes false
Or, I think, better solution is to start another background worker, considering that request of cancelation was already sent to the first one, so it will be soon or later stop. In this case, you need to know from which background worker data comes, in order to process it or not, cause on start of second the first one will still run and pump the data from WebService.
Hope this helps.
CancelAsync returns before the worker cancels and stops its work. Hence, your RunWorkerAsync call is starting before the worker is ready, and you're getting that error. You'll need to wait for the worker to be ready first.
When I'm not interested in tracking progress of an async operation, I tend to prefer to just slap a lambda at ThreadPool.QueueUserWorkItem instead of instantiating and setting up a background worker that I have to check the state of to be able to reuse in a sane way.
You need to verify before you kicks in.
f( !bw.IsBusy )
bw.RunWorkerAsync();
else
MessageBox.Show("Can't run the bw twice!");
You are calling CancelAsync without waiting for the background worker to actually cancel the work. Also you must have your own logic for cancelling the work. There is a good example on MSDN which shows how to do it. Basically in your parseanddownloadresult() method you need to check the CancellationPending property.
References:
http://msdn.microsoft.com/en-us/library/ms171728.aspx
http://stackoverflow.com/questions/5408155/how-to-make-delegate-thread-sta
I wanted to create a new Thread and make it STA therefore I was not able to use asynchronous delegates or BackgroudWorker (as mentioned in references link 1) Therefore I end up creating a Thread of my own make it STA and attach a callback to know when the Task is complete. The code is something like below and even though I am using invoke required, I still get InvalidOperationException (once in a while)
delegate UpdateEventHander(Object sender, EventArgs e);
class MyTask{
// to generate an event
public event UpdateEventHandler Finished;
public void Start(){
Result = // something that require the thread to be STA.
Finished(this, EventArgs.Empty);
}
public Result GetResult(){
return Result;
}
}
Class Foo : Form{
// It has many UI Controls obviously
public void doSomething(){
MyTask task = new MyTask();
task.Finished += new UpdateEventHander(CompletionHandler);
Thread thread = new Thread(new ThreadStart(task.Start));
thread.setAppartmetnState(AppartmentState.STA);
thread.start();
}
public void CompletionHandler(Object sender, EventArgs e){
MyTask task = (MyTask) sender;
if (oneOfMyControls.InvokeRequired){
delegateToUpdateUIconrols del = new delegateToUpdateUIconrols(updateUIControls);
del.invoke();
}else{
UpdateUIControls();
}
}
public delegate void delegateToUpdateUIconrols();
public void UpdateUIControls(){
// It updates UI controls
// Datagrid view value properties like backgroud color and stuff.
// change text in the label.
}
}
Question 1: Which thread will UpdateUIControls execute ? - if you say "Main UI Thread" - then in that case how will the system know if its supposed to run in Main UI thead and NOT some OTHER thread? I am not passing any reference (about Main UI thread) when I call invoke() .. so invoke() is technically executed on the same thread..
Question 2: Once in a while, I get the InvalidOperationException. Exactly this one
http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/6b450a21-e588-414a-afae-9adabfd03674/
If UpdateUIControls is executing in the main UI thread, there should be not prblem, Right? So, I guess answer to my question really depends upon question 1.
I will appreciate if someone share his/her wisdom on this
Karephul
Controls have thread-affinity; you can only safely talk to them from their creating thread.
You are checking InvokeRequired; however, you are mixing up Delegate.Invoke (runs on the current thread) with Control.Invoke (runs on the UI thread); very different meaning. It should be:
oneOfMyControls.Invoke(del [, args]);
I have a worker thread that needs to add items to a BindingList. However, the BindingList is databound to a DataGridView. So, when I try to add to the list, I get an InvalidOperationException (Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on.)
Normally for this exception you would do:
if(winformControl.InvokeRequired) {
winformControl.Invoke(MethodDelegate);
}
However, the databinding confuses things, as there is no Winform control in sight. All I have is the following line, which throws the exception:
ClassInstance.MyBindingList.Add(myObject);
If you have a solution specifically for this scenario, great.
If not, how can I get the worker thread to tell my main thread to perform a particular method (with several parameters supplied by the worker thread)? This may be a preferable option, since my worker thread is actually doing a bunch of stuff at the moment (like writing to the database), and I'm not sure if everything is thread-safe. I'm a student, and new to multithreading, and it really is not my forte yet.
One option here is to tell BindingList<T> to use the sync-context, like this - however, this is arguably not the best approach. I wonder if you could expose your data via an event or similar (rather than adding to the list directly) - then have your UI handle the event by sending to the right thread and adding to the UI model.
In your worker class constructor, try this:
private System.Threading.SynchronizationContext mContext = null;
/// <summary>
/// Constructor for MyBackgroundWorkerClass
/// </summary>
public MyBackgroundWorkerClass(System.Threading.SynchronizationContext context)
{
mContext = context;
}
Then, when you need to invoke something on the UI thread:
private void CallOnTheUiThread(object dataToPassToUiThread)
{
// Make sure the code is run on the provided thread context.
// Make the calling thread wait for completion by calling Send, not Post.
mContext.Send(state =>
{
// Change your UI here using dataToPassToUiThread.
// Since this class is not on a form, you probably would
// raise an event with the data.
}
), null);
}
When creating your worker class from a form on the UI thread, this is what you would pass as the synchronization context.
private void Form1_Load(object sender, EventArgs e)
{
var worker = new MyBackgroundWorkerClass(SynchronizationContext.Current);
}
You can fire an event to the main, UI, thread and there have:
if (this.InvokeRequired)
{
this.Invoke(...);
}
so you are testing on the main Window itself.
BackgroundWorkers are easy to implement if you are able to given the requirements.
Define a DoWork method that runs on a background thread such as saves to the database. The RunWorkerCompleted method is called when DoWork finishes. RunWorkerCompleted runs on the UI thread, and you can update the view's list with no problems.
// on the UI thread
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += DoWork;
worker.RunWorkerCompleted += RunWorkerCompleted;
worker.RunWorkerAsync("argument");
Events:
static void DoWork(object sender, DoWorkEventArgs e)
{
e.Result = "4";
}
static void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
string a = (string)e.Result;
Console.WriteLine(a);
}
else
{
Console.WriteLine(e.Error.Message);
}
}