I'm pretty new at C# so forgive me if this is a bit of a silly question.
Anyway I'm writing a little chat application that so far seems to be going well, I've run into a problem that I seem to have fixed but I'm hoping to work out why I need to do it this way.
When I receive a message I want to update a text box so I use:
txtConnectedID.Text = "test";
But I receive this error:
System.InvalidOperationException was unhandled by user code
Message=Cross-thread operation not valid: Control 'txtConnectedID' accessed from a thread other than the thread it was created on.
Now, I think this has something to do with stopping the method running twice and not updating properly? I'm not 100% on this. So now I have a delegate for a method that accepts a string and I call:
private delegate void stringDelegate(string s);
BeginInvoke(new stringDelegate(writeToIPBox), new object[] { e.ConnectedIP.ToString() });
private void writeToIPBox(string newIP)
{
txtConnectedID.Text = newIP;
}
I'm not sure why I'm doing this, how it's any different. I'm not really happy to just do it this way without knowing why.
Thanks in advance
You should only attempt to update controls from the thread on which they were created. There are good reasons for this as it is easy to hit a race condition. These controls are not thread safe and this is the runtime helping you out a bit.
Instead, as you have figured out, you need to update it on the UI thread, which is what BeginInvoke is doing; calling the delegate asynchronously on the UI thread.
All controls are owned by the UI thread (of which there is only one), and trying to access them from another thread results in this Exception.
BeginInvoke calls the specified delegate on the UI thread, which is why it works. You can also check if you are on the UI thread by checking txtConnectedID.InvokeRequired.
It took a new minutes of determined googling but I eventually found an "authorative" reference:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invokerequired%28v=vs.90%29.aspx
The short answer (to quote from that page) is: Controls in Windows Forms are bound to a specific thread and are not thread safe.
This article also aludes to how this can be handled "internally"... if control.InvokeRequired (i.e. we've been called from another thread) handle the invocation internally... rather than spreading delegates throughout your code.
Personally I'd never really questioned WHY a delegate was required, I just did it. Good question.
Cheers. Keith.
private static void SetText(TextBox tb, string text)
{
if (tb.InvokeRequired)
{
var stDelegate = new StDelegate(SetText);
tb.Invoke(stDelegate, new object[] { tb, text });
}
else
{
tb.Text = text;
}
}
Related
I am working on application which calls a function populate() on window_load
This function execution takes about 1 minute to complete. I wanna make this function to be called in separate thread.
I am using following code
Thread thread = new Thread(PopulateAndDrawGraph);
thread.Start();
in this function at last line is
nodeXlControl1.DrawGraph(true);
Here exception occurs
The calling thread cannot access this object because a different thread owns it.
what wrong is actually occurs
You are only allowed to access UI controls from within the same thread that created the control, which is usally the UI thread itself.
You will need to change your code in a way that it becomes thread-aware.
Here is a fantastic article that was published in the MSDN magazine: Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads, which will explain in great detail how you can do what you want to do.
The article is a little old, but the same principles still apply.
I guess that the newer C# language features - like the new async / await keywords - should make your task a little easier.
But keep in mind that the same old limitations for accessing UI controls still exist. There is no way around to understand the basics as descripbed in the article.
There are 2 ways to handle this, a correct way and another way:
1: If you want a good working solution then this should do the trick, ..
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;
}
}
msdn.microsoft.com/en-us/library/ms171728.aspx
2: but if you want a quick solution without guarantee that it will work properly, just set this variable:
Control.CheckForIllegalCrossThreadCalls = False
The problem is, that the any UI interaction has to occur in the main thread of the application. My advice is that you draw make the computation in the separate thread, but collect your results in an object. After the thread is finished you can extract the result from that object, and place it the nodeXlControl1.
Sadly i don't know the details about your objective, the sum of the solution is this.
If u can tell more details about this i may be able to further help you.
check this article (How to: Make Thread-Safe Calls) here out. It explains all the related items regarding cross thread calls. I also recommend that you look into the concept of "Task" in c#. Theres a very well written lib that helps you handle parallelism and similar concepts!
Since you are trying to access a UI element from a thread you need to call Invoke. So in PopulateAndDrawGraph you should have:
Invoke((Action)(() =>
{
nodeXlControl1.DrawGraph(true);
}));
You can't access the UI controls from any other thread except the thread which has created this control(you can access/change the UI control properties from the main UI thread only). Go through this link, i hope this makes you clear Cross-thread operation not valid
I have used BackgroundWorker class in c#.
I've found what looks like a very simple solution to my current situation.
My current situation is that I want to do some I/O-heavy operations on a new Thread, so that I do not bog down my GUI Thread. I have a function written, as a member of my Form, that does these I/O operations already, but running it on the GUI Thread really makes the application a pain to use. So my plan was to just run this function in a new Thread. So, I created a Thread variable, in my form, and am trying to get it to use that function as the ThreadStart parameter. It does not seem to like it, though.
I found an elegant looking solution, as a response to another thread, here.
///...blah blah updating files
string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate {
someLabel.Text = newText; // runs on UI thread
});
///...blah blah more updating files
From the looks of that response, I could run this function in a new Thread and then use an anonymous function to update my Form when the Thread has finished its calculations. I'm just not good enough to fill in the blanks from that response, though.
Everything I seem to read about Threads says that my ThreadStart function needs to be a static method in a new class. That response seems to suggest that I can do it within my Form class though, so that the this reference still references my Form instance. Otherwise, if my ThreadStart parameter were a different class, I'd have to pass in references to the Form instance, and that seems like more code, right?
Would anybody mind helping me fill in the context for that response? Thanks in advance!
There are a lot of ways you can do this. A very simple, straightforward one that's been around for a number of versions is to use the BackgroundWorker. It is designed for exactly this case. It has a DoWork method that runs in a background thread, and a Completed event that is fired after the work is done which runs in the UI thread (so you don't need to call invoke or anything to update the UI with the results). It even has support built in for reporting progress (the report progress event also runs in the UI thread) so you can easily update a progress bar or status text.
MSDN has some examples as well, and you can find lots more through some simple searches.
Another option, made available through C# 4.0, is to use Tasks. You can start a new task which will be run in a background thread, and then you can add a continuation which will be in the UI thread.
Here is a simple example:
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew(() => doStuffInBackground())
.ContinueWith(task => updateUI(), TaskScheduler.FromCurrentSynchronizationContext());
}
private void updateUI()
{
throw new NotImplementedException();
}
private void doStuffInBackground()
{
throw new NotImplementedException();
}
You can of course do whatever you want in the actual lambdas that I have there, or you could even remove the lambdas and put methods in there directly as long as you ensure the signatures are correct. You could also continue chaining these continuations if you wanted, allowing you to, for example, to task 1, update a label, then do task 2, update a label, etc. The main disadvantage is that it's not good at updating a progress bar frequently inside of a loop, the way a BackgroundWorker can.
I'm running heavvy background work with Parallel.Invoke, after all processing has completed I return the method, return again, call a next method to utilize the calculated data I get the error:
Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
But I already returned from the threads that where created by Parallel.Invoke to the one that called it in the first place. Is it normal that control does not revert to the thread where it started? And how can I assure that this does happen?
Code:
public void TopMethod()
{
Calculate(4);
UpdateGui();
}
public void Calculate(int depth)
{
Recursive(depth);
}
public void Recursive(int depth)
{
if (depth > 0)
System.Threading.Tasks.Parallel.Invoke(
delegate { Recursive(depth - 1); });
}
public void UpdateGui()
{
CalculateOhter(); // Works fine.
controlElement.Focus(); // Causes exception
}
Edits:
I know about Control.Invoke But this would be an ugly solution (don't want to store a delegate in every control) and the program needs to wait for all computations to complete before it can continue. So it would be better if I could somehow force control to return to the thread that I started out with in the first place.
You need to access a control/window from the thread that created that control. Use Control.Invoke and Control.InvokeRequired.
The horrible way to do is to set Control.CheckForIllegalCrossThreadCalls to false. It should get rid of your error, but it is not good design.
The problem with that occurs when a thread other than the creating thread of a control tries to access one of that control's methods or properties, it often leads to unpredictable results.
The example for Control.Invoke involves storing a Delegate, can this be done without storing a delegate for every control? So passing the method I need to call as an argument to the Invoke call (tried this but cannot get it to work).
The program needs to wait for all computations to complete before it can continue so it would be better if I could somehow force control to return to the thread that I started out with in the first place.
I don't understand why this does not happen at default, why does it not just return to the thread it started with when everything is complete?
Solved it, turns out I created a seperate thread to call the given code in the first place, kinda stupid :P
I'm a newbie to C# and multithreading so I apologise if this is a duplicate question but being a novice it appears my question is slightly different to others I have read.
My GUI runs in one (main) thread. It calls a background task (in a dll -- that I am writing too) that runs in a separate thread. The dll has NO knowledge of the GUI (i.e. it cannot reference the GUI class).
Now, say I want to update a progress bar on the GUI based on the status of the dll thread -> What I'm doing is creating an event in the dll that every X per cent will fire and the GUI will subscribe to this event. When the event is fired, the GUI will update the progress bar.
My questions:
Is the method of creating events the best way (bearing in mind the dll cannot reference the GUI)?
How do I ensure my above method is 'event safe'? Should I pass the progress percentage in the event to be thread safe or is there more to it?
Do I need to use Invoke when updating the GUI? I saw a post that suggested I did but I don't understand why since the updating of the bar is being done in the GUI thread??!
Hope you can clarify this for me!
Thanks
1.-I use that method all the time and yes it will work
2.-Just pass a int to the event handler and the variable will be safe to read. however when you are fireing the event from code do it like this
private void UpdatePercentage(int a)
{
var myEvent = PercentageUpdatedEvent
if(myEvent != null)
myEvent(this, new ProgressBarEventArgs(a));
}
The reason for this is so if the event is unsubcribed between the null check and the calling you won't get a exception.
3.-As everyone else has mentioned you will need to call Invoke as the event will be running on the dll's thread. However with controls it is legal to call a BeginInvoke without a EndEnvoike so the call will be non blocking on the dll's thread.
Here is the pattern I always use
private myClass_OnPercentageUpdatedEvent(object a, ProgressBarEventArgs e)
{
if(progressBar.InvokeRequired)
progressBar.BeginInvoke((Action<object,ProgressBarEventArgs>)myCless_OnPercentageUpdatedEvent, a, e);
else
{
progressBar.Value = e.Value;
}
}
Look into the BackgroundWorker class. It sounds like it fits your scenario pretty well.
This link on MSDN explains how to use it: http://msdn.microsoft.com/en-us/library/cc221403%28VS.95%29.aspx
Keep in mind that under most circumstances, the events raised from your background task will also run on the background thread. No thread context switch happens automatically at all.
To understand why, you have to consider what an event is; just a certain type of Delegate object. You are setting a Delegate to that event from the main thread... but that delegate will actually be called within the background thread, in the code that triggers the event.
So yes; you would need to make sure you are moving things over to run on the GUI thread from within that event handler.
To answer (3) you will need to use Invoke. The event-handlers are going to be run from the background thread, not the GUI thread.
If you spin off a thread, you need to create a delegate, that can safely invoke your main thread with the appropriate parameters.
delegate void UpdateDelegate(int val)
void Update(int val)
{
if(this.InvokeRequired())
{
Invoke(new UpdateDeleage(Update),new object[] {val});
return;
}
this.MyProgressBar.Value = val;
}
Call Update from your separate thread as you would if calling it from your main thread. Once the thread determines that your main thread needs invoked to pass the value, it will invoke it with your delegate, with the parameters you passed. Otherwise, it will simply skip the block and set your values.
e.g.
...
new Thread(()=>IncrementValues()).Start();
...
void IncrementValues()
{
while(true)
Update(new Random(0,10));
}
I have on my blog a few different approaches to this problem, with the advantages/disadvantages of each. In summary, I recommend using the Task class.
I've been researching on how to do this for about a week and I'm still not sure about the correct approach, in some examples I see the Thread class is used in others I see Invoke is used which has confused me a bid.
I have a GUI program in c# which contains a textBox which will be used to give information to the user.
The problem I'm facing is that I'm not sure how I can append text to textBox from another class which is running on another thread. If someone can show me a working example, it would help me greatly.
Best Regards!
Easy:
MainWindow.myInstance.Dispatcher.BeginInvoke(new Action(delegate() {MainWindow.myInstance.myTextBox.Text = "some text";});
WHERE MainWindow.myInstance is a public static variable set to the an instance of MainWindow (should be set in the constructor and will be null until an instance is constructed).
Ok thats a lot in one line let me go over it:
When you want to update a UI control you, as you say, have to do it from the UI thread. There is built in way to pass a delegate (a method) to the UI thread: the Dispatcher. I used MainWindow.myInstance which (as all UI components) contains reference to the Dispatcher - you could alternatively save a reference to the Dispatcher in your own variable:
Dispatcher uiDispatcher = MainWindow.myInstance.Dispatcher;
Once you have the Dispatcher you can either Invoke() of BeginInvoke() passing a delegate to be run on the UI thread. The only difference is Invoke() will only return once the delegate has been run (i.e. in your case the TextBox's Text has been set) whereas BeginInvoke() will return immediately so your other thread you are calling from can continue (the Dispatcher will run your delegate soon as it can which will probably be straight away anyway).
I passed an anonymous delegate above:
delegate() {myTextBox.Text = "some text";}
The bit between the {} is the method block. This is called anonymous because only one is created and it doesnt have a name - but I could instantiated a delegate:
Action myDelegate = new Action(UpdateTextMethod);
void UpdateTextMethod()
{
myTextBox.Text = "new text";
}
Then passed that:
uiDispatcher.Invoke(myDelegate);
I also used the Action class which is a built in delegate but you could have created your own - you can read up more about delegates on MSDN as this is going a bit off topic..
Sounds like you're using a background thread for processing, but want to keep the UI responsive? The BackgroundWorker sounds like the ticket:
The BackgroundWorker class allows you
to run an operation on a separate,
dedicated thread. Time-consuming
operations like downloads and database
transactions can cause your user
interface (UI) to seem as though it
has stopped responding while they are
running. When you want a responsive UI
and you are faced with long delays
associated with such operations, the
BackgroundWorker class provides a
convenient solution.
Just use BackgroundWorker for the same. It is simple and takes away the pain of managing threads of your own. for more, you can see: http://dotnetperls.com/backgroundworker