I have a background thread and a main thread for a loop in my C# application.
On this background thread I have to access Text from the UI and Parse it to be used in conditional statements as seen below:
int i = 0;
int loopNum = int.Parse(Loop_Number.Text); //< What I need to access
if (loopNum > 0)
{
while (i < loopNum)
{
//DoStuff
}
}
Problem being the above statements are being run in the background thread not the main UI thread. So the program throws an InvalidOperationException for that int.Parse statement.
Exception has this information:
Additional information: The calling thread cannot access this object because a different thread owns it.
So being that I only have a UI thread and this thread run temporarily how can I access Loop_Number.Text within this background thread.
I have tried the below code:
Dispatcher.BeginInvoke((Action)(() => { int loopNum = int.Parse(Loop_Number.Text); }));
but then loopNum is out of context for the conditional statements.
The short answer, which will resolve the immediate problem, is that you need to use Invoke() instead of BeginInvoke():
string text = (string)Dispatcher.Invoke(() => Loop_Number.Text);
int loopNum = int.Parse(text);
Now, that said, there are signs in your question that you're simply doing it all wrong. :(
First, you should not need to access a UI object directly at all. I.e. from the exception you report, the Loop_Number variable presumably is a reference to some WPF control, like a TextBox. Instead, you should be following some kind of data binding pattern, like MVVM, in which you have a non-UI object which has a property bound to the UI control, and which receives the desired number.
Doing it that way, not only would the property be set in the UI thread, allowing the value to be accessible elsewhere (such as in a different thread) without invoking on the UI thread, you can make the property have the type int and WPF will convert from the text for you. You wouldn't even need to call int.Parse() at all.
Second, while it's hard to say for sure without a good Minimal, Complete, and Verifiable example showing clearly what you're doing, I doubt it's really a good idea for the thread to be retrieving the value at all. Instead, the value is something that your UI thread should have retrieved at the point in time that it decided it needed to start this background operation, and the value should be passed to the background operation somehow (stored in a data structure specific to the operation, passed to an async method that represents the operation, etc.).
Bottom line: while the code at the top of this answer will address your specific concern, I doubt it's really the way this code (whatever it is) ought to be written. You should probably consider revisting your design, so that it works more smoothly, without the need for calling Invoke() at all.
Related
A common exception one can get when working with multiple threads in WPF is:
The calling thread cannot access this object because a different thread owns it
What are the options to deal with this properly?
Depending on the situation there are various options:
Accessing a control from another thread
e.g. updating a TextBlock with progress information.
Data Binding:
In this case the easiest thing you can do is avoiding the direct interaction with the control. You can just bind the property you want to access or modify to an object whose class implements INotifyPropertyChanged and then set the property on that object instead. The framework will handle the rest for you. (In general you rarely should need to interact with UI-elements directly, you can almost always bind the respective properties and work with the binding source instead; one case where direct control access may be necessary is control authoring.)
There are some cases where data binding alone is not enough, for example when trying to modify a bound ObservableCollection<T>, for this you need...
Dispatching:
You can dispatch your accessing code to the thread owning the object, this can be done by calling Invoke or BeginInvoke on the Dispatcher owning the object being accessed (getting this Dispatcher is possible on another thread).
e.g.
new Thread(ThisThreadStart).Start();
void ThisThreadStart()
{
textBlock.Dispatcher.Invoke(new Action(() => textBlock.Text = "Test"));
}
If it is not clear on which thread a method is executed you can use Dispatcher.CheckAccess to either dispatch or execute an action directly.
e.g.
void Update()
{
Action action = () => myTextBlock.Text = "Test";
var dispatcher = myTextBlock.Dispatcher;
if (dispatcher.CheckAccess())
action();
else
dispatcher.Invoke(action);
}
If an object is not a DispatcherObject and you still need the associated Dispatcher you can use Dispatcher.CurrentDispatcher in the thread creating the object (so doing this in the method being executed by a thread will not do you any good). For convenience as you usually create objects on the application's main UI thread; you can get that thread's Dispatcher from anywhere using Application.Current.Dispatcher.
Special cases:
BackgroundWorker
Move any control access to ProgressChanged as it occurs on the thread that created the instance (which should of course be the UI-thread)
Timers
In WPF you can use the DispatcherTimer for convenience, it does the dispatching for you so any code in Tick is invoked on the associated dispatcher. If you can delegate the dispatching to the data binding system you of course can use a normal timer as well.
You can read more about how the Dispatcher queue works and WPF threading in general on MSDN.
Accessing an object created on another thread
e.g. loading an image in the background.
If the object in question is not Freezable you should in general simply avoid creating it on another thread or restricting access to the creating thread. If it is Freezable you just need to call Freeze to make it accessible to other threads.
Accessing a data object from another thread
That is, the type whose instance is being updated is user-code. If an exception is thrown this situation probably came about by someone using DependencyObject as base type for a data class.
This situation is the same as accessing a control and the same approaches can be applied but usually it should be avoided in the first place. Granted, this allows for simple property change notifications via dependency properties and those properties can also be bound but often enough this is just not worth giving up thread-independency. You can get change notifications from INotifyPropertyChanged and the binding system in WPF is inherently asymmetrical, there always is a property that is bound (target) and something that is the source for this binding. Usually the UI is the target and the data is the source, meaning that only UI components should need dependency properties.
That would be several hundred lines of code, for something I "figured out".
But the summary is:
App_OnStartup
generate a background thread
in the callback,
Call
Application.Current.MainWindow.Dispatcher.CheckAccess() - gets the exception
Application.Current.Dispatcher.CheckAccess() does not
I have a udp listener object that communicates through events where the method/callbacks are +='ed in my mainWindow wpf .cs file.
The event handler functions are called with parameters, one being the message I want displayed in a listbox in the mainWindow.cs
Using the information in this thread by H.B. above;
I have added, tested and handled the crossthread in wpf in my eventhandler callback using the following code, but I use a real message not a hard coded one:
listBox1.Dispatcher.Invoke(new Action(() => listBox1.Items.Add("MessageHere")));
UPDATE:
This is better because you can put more things in the anonymous function.
listBox1.Dispatcher.Invoke((Action)delegate
{
listBox1.Items.Add(e.ReaderMessage);
});
I really don't know how to properly get data from a Thread.
In a thread (or Task, doesnt matter) i want to calculate a lot of doubles. When this is finished i want to show this data in a grid and in a graphic-chart. So i tried to return some type of
Observable<List<double>>
When i then wanted to create a "new ViewModel(data)", i get exceptions cause of threads.
So how do i properly get such a list back from a thread and use it in UI?
Or maybe pass this data while calculating to show some live values would also be nice..
thanks for answers, just need a few tips
This kind of functionality is common and is often accomplished using the BackgroundWorker Class. There is a code example on the linked page and you can find another with feedback in my answer to the How to correctly implement a BackgroundWorker with ProgressBar updates? question on this website.
Alternatively, you can use the Dispatcher object from the UI thread to pass values to that thread. Note that each thread has it's own Dispatcher, so be sure to call the one from the UI thread. You can use this little helper method:
public object RunOnUiThread(Delegate method)
{
return Dispatcher.Invoke(DispatcherPriority.Normal, method);
}
You can use it like this:
RunOnUiThread((Action)delegate
{
// You can run any number of lines of code on the UI Thread here
});
Or inline, like this:
RunOnUiThread((Action)delegate { UpdateData(); });
I have this method in a separate class that has constructors like this:
private UiThreadManager(Dispatcher dispatcher)
{
Dispatcher = dispatcher;
}
public UiThreadManager() : this(Dispatcher.CurrentDispatcher) { }
I call this constructor on the UI thread to ensure that the Dispatcher that I will be using is in fact the Dispatcher from the UI thread.
I use a BackgroundWorker and do this:
private void loadNewAsyncToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Text = "RunWorkerAsync()";
backgroundWorkerLoading.RunWorkerAsync();
}
private void backgroundWorkerLoading_DoWork(object sender, DoWorkEventArgs e)
{
UnsafeThreadMethod("hello");
EvenUnsaferThreadMethod();
}
And now the two methods.
private void UnsafeThreadMethod(string text)
{
toolStripLabelRssFeedData.Text = text;
}
private void EvenUnsaferThreadMethod()
{
panelLoading.Visible = true;
}
I don't understand why UnsafeThreadMethod doesn't throw the following exception but EvenUnsaferThreadMethod does.
Cross-thread operation not valid: Control 'panelLoading' accessed from a thread other than the > thread it was created on.
According to the message it's because toolStripLabelRssFeedData was created on the same thread but it wasn't.
I thought that I can't call controls created by the main thread and have to use the ProgressChanged event. What's going on?
And I have a second question. What is the advantage of doing it like this when I can use ProgressChanged? What should I do?
private void EvenUnsaferThreadMethod()
{
if (panelLoading.InvokeRequired)
{
panelLoading.Invoke(new MethodInvoker(() => { EvenUnsaferThreadMethod(); }));
}
else
{
panelLoading.Visible = true;
}
}
To the first question:
the cross-thread exception is deliberately thrown in Debug mode. This means there is (conditional) code checking on InvokeRequired built into most of the GUI controls. Like the Panel.
Apparently the ToolstripLabel does not make this check. Since it does not derive from Control that could be because it is outside the scope of this safety net.
Since the standard disclaimer "Any instance members are not guaranteed to be thread safe" applies to the ToolstripLabel I would just go with the normal InvokeRequired logic when setting the Text.
For your first question, I am not entirely sure, but a review from online seems to show that sometimes this will not throw an exception, but it will not update the label. Is that the case here? Is your label being updated along with having no exception?
However, I can answer you second question right now. The ProgressChanged event is meant for exactly what it sounds like. It is supposed to be called to let the UI thread know the status of the backgroundworker so that it can update itself appropriately. The original calling thread (UI in this case) is the one that is used for the ProgressChanged, so when it updates it does not need to call Invoke. But, this should really only be done for showing the progress of a background worker.
Now, if it is not an update that you are trying to pass to the calling method, then I would suggest just passing your return data back through the RunWorkerCompleted event. This passes all of your final data back up to the original (UI) thread, so that it can update the UI without any need for an Invoke.
So, yes your call to Invoke will work, though. However, understanding what each of the other events are for can help you understand why to use one way over another. Maybe a ProgressChanged event fits better? It can also declutter your code from having unnecessary invokes.
Update to first q
I still cannot find anything about the toolstrip not needing the invoke. In fact I am finding the opposite using google searches like "toolstriplabel no cross thread exception" or "toolstriplabel invoke", etc. However, as henk mentioned, the toolstriplabel doesn't inherit from control so that might explain why no invoke is required. However, my suggestion is to assume that it will act like any other UI control and make sure it is updated on the UI thread to be safe. do not rely on quirks. Better safe than sorry, you never know if things like this might change, especially since it is logically a UI item to most..,
The advantage of your second choice is that it works :)
All UI elements are created on main UI thread and, what is more important from this question perspective, is that can be acessed only within that thread.
This is the reason why your first case fails and that is the reason your second case will work. Invoke()... will redirect required merhod call to the main UI thread.
Hope this helps.
How can I force a section of code to be executed on my main thread?
This is why I'd like to know:
I have a custom created message box that at times gets shown from a thread that is not the main thread. However, when the message box constructor is called I get an InvalidOperationException saying "The calling thread must be STA, because many UI components require this." This makes sense, UI elements need to be handled on the main thread.
My MessageBox.ShowMessage(...) function is a static function that creates an instance of my custom message box and shows it. Is there a something I could put in ShowMessage that would force the message box to be created and shown on the main thread? Elsewhere in my code I use the Control.BeginInvoke to handle similar issues, but since it is a static function there is no already existing UI element for me to call BeginInvoke on.
Do I have to call MessageBox.ShowMessage from with a Control.BeginInvoke? I'd prefer the BeginInvoke (or some equivalent) to be called from within ShowMessage.
There are a few options here:
make the second thread STA (you can only do this for your own Thread - not for ThreadPool threads) - via .SetApartmentState(ApartmentState.STA);
see if SynchronizationContext.Current is non-null; if so, use Send/Post
pass the form/control in as an ISynchronizeInvoke instance (may not apply to WPF - I'm not 100% sure)
Your thinking is right -- in order to get it to work properly, you're going to need to get it called from the main thread.
The simplest way? When you start your main form, save a reference in a static variable that is visible to your ShowMessage() call. Then, your ShowMessage can do the standard:
if(myForm.InvokeRequired)
{
myForm.Invoke(() => ShowMessage(arg1,arg2,arg3));
return;
}
.... other code here....
Instead of directly showing the message box, just send a message to your main thread, which signals the main thread to display a message box.
Here is the problem I have: I need to make sure an object is instantiated on the UI thread. If it is not, it should throw an exception. But how do I check inside a method whether it is running on the UI thread? Note: I do not want to pass any information into the object's constructor.
The perfect candidate would be the DispatcherSynchronizationContext (WPF implementation of SynchronizationContext) which internally holds a reference to Dispatcher which references the thread it's associated with, but unfortunately that field is private so there is no way for me to access it.
Small clarification, although there is typically only 1 UI thread there can be many UI threads. This is true for both WPF and WinForms.
The best way I've found to achieve this though is with a SynchronizationContext. Both WPF and WinForms will establish a SynchronizationContext on any thread they are running UI on. This is the function I use if I am not tied to any particular UI model.
public bool IsPossiblyUIThread() {
return SynchronizationContext.Current != null;
}
Note, it is not in any way foolproof. It's possible for non-UI components to establish a SynchronizationContext and this would return true for a simple worker thread. Hence the non-authoritative name.
A slightly more reliable way to do this is as follows. But it requires you to reference at least a portion of WPF to implement.
public bool IsLikelyWpfUIThread() {
var context = SynchronizationContext.Current;
return context != null && context is DispatcherSynchronizationContext;
}
Dispatcher.CheckAccess() returns true if your code runs on the same Thread as the Dispatcher. It should work if there is only one Dispatcher/UIThread.