This question already has answers here:
Why can I access controls from a form in a thread without cross thread exception?
(3 answers)
Closed 6 years ago.
I'm writing the functionality for a particular part of a program which requires me to change the text of a number of labels on a window continuously until the user presses a button. I've created a thread to accomplish this, and quickly noticed that the runtime complains about cross-thread accessing of a label, giving the error message:
Cross-thread operation not valid: Control 'label2' accessed from a thread other than the thread it was created on.
I've since learned that accessing a System.Windows.Forms.Control member in a thread other than the UI thread is not allowed. However, my question has to do with the following simplified code:
private void myThread(){
//'labels' is an array of all labels on my form
Label currLabel = labels[rand.Next(0, labels.Length)];
currLabel.BackColor = Color.Yellow; //This works with no complaint from the runtime
currLabel.Text = "Hello"; //This causes the previously-mentioned error
}
So, why am I able to change some properties of labels in a thread other than the UI thread, but others are just off-limits? Am I missing some larger concept here? Any help is appreciated.
UI Components can only be accessed on the Main UI Thread. Therefore, as Multi-threading behaves, is creating a new thread on your system. By doing so, the Main UI's thread is not blocked. That being said, if you want to access the Controls, you need to call Invoke or BeginInvoke Methods, depending on the condition
sample:
Label1.BeginInvoke((Action) (() => { \\Multiple Line of codes here. }));
Also, check on these links:
Accessing UI Control from BackgroundWorker Thread
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
Solve a cross-threading Exception in WinForms
ADDED NOTE: while it is possible not to use Invoke Methods at some point, it is still better safe than sorry. some may eventually throw such exceptions on runtime. depending on the situation the program is running at
Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way.
So, why am I able to change SOME attributes of labels in a thread other than the UI thread, but others are just off-limits?
Possibilities include:
Because someone forgot to add the cross-thread check
Changing that attribute doesn't happen to trigger some piece of code that required to UI thread.
Someone forgot to add the cross-thread check and changing that attribute triggers some piece of code that required to UI thread, but you got lucky and the program didn't crash this time.
Since 2 and 3 are indistinguishable 99% of the time, you may think that you are in situation 2 when really you are in situation 3. Which means random crashes that you can't reproduce.
Related
I'm kind of in a learning process of multi-threading, so while I was playing around with Task class I notice some strange (for me) behavior with calling Task.Factory.StartNew method. I was doing some example in WPF application, where I just tried to call a method from Task on button click and what I have done is this:
Task.Factory.StartNew(() => OrderTickets(numberOfTicktes, cbMovieName.Text));
However the method OrderTickets was never called. Then I changed my code like this:
string movieName = cbMovies.Text;
Task.Factory.StartNew(() => OrderTickets(numberOfTicktes, movieName ));
After this change my method "OrderTickets" was called as I expected. My question here is why I can not use values of UI controls, like I tried with my combobox, directly inside of Task StartNew method? My guess, this is because UI controls are hold by the UI thread but not quite sure about it. Can anyone explain to me what really happens here? Could you point me to Microsoft documentation describing this?
If your code could speak, this is what you are doing:
"I am the main thread. Hey scheduler can you please do this work for me using a thread from the pool?"
() => OrderTickets(numberOfTicktes, cbMovieName.Text)
Main thread is now free and sometime later (or immediately) a pool thread picks up this work to do it and notices this:
cbMovieName.Text
So it tries to communicate with cbMovieName but the thread (in your case main thread or UI thread) intervenes and says:
"Hey pool thread, why are you trying to communicate with one of my controls without asking me? Go away!!! (As he rolls his eyes)"
Exception is thrown on the pool thread but not bubbled up. Therefore, it seems to you like it was never called but all the above happened.
In the other case where you have this:
string movieName = cbMovies.Text;
() => OrderTickets(numberOfTicktes, movieName)
It will work because the pool thread does not need any UI control.
It isn't immediately obvious, but the issue is that you are running into two issues - and they way they interact is a bit confusing.
Issue 1 - you can't access controls outside of the UI thread.
Issue 2 - StartNew doesn't bubble up exceptions in the way you are expecting it to. ContinueWith may assist with this.
The net result is it looks to you like the call failed without throwing an exception.
I get an InvalidOperationException with the message,
Control control name accessed from a thread other than the thread it was created on.
The "Control" was created by a Thread that is no longer doing any work, and I don't know how if I can invoke the method or if I should create a local variable that contains the value of the tags_richtextbox.
The class that is Giving the error:
public class details_Panel
{
public string tags
{
get { return tags_richtextbox.Text; }
set { tags_richtextbox.Text = value; }
}
private RichTextBox tags_richtextbox = new RichTextBox() { DetectUrls = false, Dock = System.Windows.Forms.DockStyle.Fill, ReadOnly = true };
}
The Function Giving the error:
detailsPanelSaveData.tags.AddRange(ent.detailspanel.tags.Split(','));
So should I Invoke or add a private variable that has the same value as tags_richtextbox.Text and if I should invoke, whats the syntax to do that and wait for it to finish? (I'm still learning about threading and figuring out the correct invoke syntax)
Edit: DetailPanelSaveData.tags is a List<string> Just realized I wasn't very clear
So to Clarify I'm trying to save a split string in a list of strings.
The "Control" was created by a Thread that is no longer doing any work
Without a good, minimal, complete code example that reliably reproduces the problem, it's impossible to completely understand the problem. However, the idea of there being a thread in which some instance of a control was created, and yet that thread "is no longer doing any work" is a bit weird, to say the least.
Control objects must only be created in a thread that is running the UI. In most programs, there is only one such thread and there should only ever be one such thread. If you have code running in a different thread, and due to some operation in that thread there is a point at which a control needs to be created, you need to transfer execution to that one UI thread for the purpose of creating the control, so that the control is created in the correct thread.
Without a better code example, it's not clear what else you need to fix. But for sure, you need to change the code where that control is created so that you're creating the control in the UI thread.
Elsewhere, e.g. when you are converting the text into an array of tags to add to your list, you may also need to transfer execution to the UI thread when you access the tags property where the tags_richtextbox object is accessed. Or maybe you won't. That all depends on where that code is executing; if it's in the UI thread, then you're all set. Otherwise, yes…you need to wrap the operation in some kind of cross-thread invocation.
NOTE: nowhere in your question are you specific about which GUI API in .NET you are using. Judging from the little bit of code and your comments in the question, I'm guessing this is Winforms. But WPF and Winrt (Store apps) have the same issue, and so the above advice applies equally in all scenarios. Of course, the exact technique for transferring execution to the UI thread varies according to API. Winforms uses Control.Invoke(), WPF uses Dispatcher.Invoke(), and Winrt uses Dispatcher.RunAsync(). It is these methods (or their asynchronous equivalents, in the case of Winforms or WPF) to which I am referring when I describe a "cross-thread invocation" or the need to "transfer execution to the UI thread".
NOTE: there is one exception to some of the above, which I hesitate to even mention because I doubt it applies in your scenario, and even if it does, the first thing you should do is fix your code so it doesn't. But for completeness, I am compelled to mention that a program can have more than UI thread running at a time. This is rare and should be avoided, but it can happen. If it does, then it is possible that one control object could be created in a thread that is legitimately different from that which owns some other control object. Now, in that case it would be your responsibility to ensure that any given UI thread continues to run for as long as any of the objects it owns still exist, and you will still need to use the GUI API's mechanism for transferring execution to a different thread when that object is accessed by a thread other than the one which owns it. But that is a way that, even though still not correct, you could wind up with a control object that was created by a UI thread that "is no longer doing any work".
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 see a lot of threads on google/here on UPDATING a UI element from another thread.
What if I want to just get the value of a checkbox?
Am I able to do this without having to do anything special?
Edit: It seems I have to take back what I wrote before. Tried the following:
Added a textbox called myTextBox and tried to retrieve the value of the Text property:
Thread t = new Thread(
o =>
{
Thread.Sleep(2000);
string value = myTextBox.Text;
Thread.Sleep(2000);
});
t.Start();
And it seems that the app (WPF) crashes after 2 seconds. Using the dispatcher works:
Thread t = new Thread(
o =>
{
Thread.Sleep(2000);
myTextBox.Dispatcher.BeginInvoke(
(Action)(() => { string value = myTextBox.Text; }));
Thread.Sleep(2000);
});
t.Start();
Thus, you still need to go through the dispatcher thread when reading values from GUI components, at least in WPF.
Second edit: This gets better. Apparently repeating the experiment for classic WinForms reveals that it works to read the Text property without using Invoke/BeginInvoke. Interestingly enough, it seems that also setting the property works fine (without invoke), although I'll wager it's not thread safe and the app doesn't complain for some reason.
Bottom line: It's a good idea in any case to use the dispatcher when interacting with GUI components from other threads, as it ensures the reads/writes are serialized to a single thread and so you have no thread-safety issues.
Can you access UI elements from another thread? (get not set)?
No.
Here is the deal. UI elements have very strict thread affinity requirements. This means you can only access the element from the thread hosting it. This includes all kinds of accesses including simple reads.1
It may work fine for simple property getters, but its perceived safeness would be an accidental result of the way that particular control was implemented. Since Control instances have thread affinity they could potentially use thread local storage techniques to save some of their state which, of course, would not be compatible with different thread. Or what if the value you are trying to read is in a half-baked state? There would be no way to synchronize access to that read since the write may be occurring inside code you have no control over. And still this is ignoring subtle memory barrier problems that may arise.
Again, if it appears to work then chalk it up as an accident. Accessing UI elements from a thread than the one hosting them is a recipe for disaster. Things may fail unpredictably and spectacularly.
1There are very few exceptions to this rule. Using the ISynchronizeInvoke methods is one such exception.
You could but strictly it wouldn't be thread safe.
For instance if the property Get code would consist of multiple operations, the UI thread could act in the mean time, halfway during the Get operation, causing unexpected results.
simply read the value as you normally do. Only to update the control, you need to switch to GUI thread.
I'm having one thread that generates GUI elements in wpf. A canvas is there to draw objects (rectangles and so on ...)
This wpf thread calls another thread let's name it the calculation thread. This thread calculates the size and position and so on of the elements to be shown in the canvas.
I want to have these two parts (GUI and calculatio) run in different threads. The "calculation thread" is based on a library without references to wpf functionality.
Now I want to show intermediate data of the calculation thread displayed by the wpf thread.
I'm doing it that way:
The calc-thread fires an event (DataReady) that is implemented by the wpf-thread:
void MyRegStringObject_DataReady()
{
if (DebugMode)
MyDrawingBoard.DrawRegElements();
}
The problem now is, that an error is thrown: "calling thread cannot access this object because a different thread owns it"
There are some answered question here in stackoverflow referring to this error but none of them can help in my case.
The function DrawRegElements() wants to clear the canvas object (among other things):
curCanvas.Children.Clear();
At this position in the code the error is thrown.
It seems that the function MyRegStringObject_DataReady triggered by an event from the calc-thread is owened by the calc-thread also. But it is defined in class that the wpf-thread is based on.
How can I solve this problem? Does anyone have any idea?
By the way:
The calc-thread is called this way:
CalcElements = Task.Factory.StartNew<bool>(MyRegStringObject.CalcRegElements);
When the thread is finished I defined:
CalcElements.ContinueWith((FinishCalcRegElements) =>
{
MyDrawingBoard.DrawRegElements();
}, CancellationToken.None, TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
No problem with that. Everythins runs perfect. The function defined in ContinueWith seems to be owened by the wpf-thread.
Error message is straightforward, you are trying access an UI element from a now owning thread. Delegate this work to WPF Dispatcher which is associated with Main UI thread, which would post all messages from worker thread to UI thread:
Application.Current.Dispatcher.BeginInvoke((ThreadStart)delegate
{
MyDrawingBoard.DrawRegElements();
});
To be sure that you are using correct Dispatcher, associated with the Main UI thread, you can pass it as a parameter to the worker thread code, so just pass Dispatcher.Current from Main UI thread, otherwise calling Dispatcher.CurrentDispatcher in the worker thread will initialize a new instance associated with a calling worker thread. Or just use Application.Current.Dispatcher which would reference the Main UI thread's Dispatcher.
PS:
But it is defined in class that the wpf-thread is based on
This is wrong assumption, classes itself are not related to any thread. Concrtete class instances are. So you can create UI/non-UI related classes either in UI or worker thread.
Also important point in which thread DataReady event has been fired?