c# - running a function with another thread using UI thread items - c#

I have a public static method within a certain class. and it uses items ( combo box, datagridviews etc' ) created in the UI thread. I want to run this function from a new thread, but I get this message :
"Additional information: Cross-thread operation not valid: Control 'CompanycheckedListBox' accessed from a thread other than the thread it was created on."
how can I run the function from a new thread and still use these items created in the UI thread?
I used this line of code : new Thread(delegate () { functionName();}).Start();

The short answer: you can't. Those components can only be accessed on the ui thread. But there are ways to dispatch you function call onto the ui thread.
Most ui frameworks are completely single threaded. It is not allowed to access any component from a different thread than the ui thread.
You need to dispatch to current ui thread. Looking it at your control names, it looks either like a winforms or a WPF application.
In WinForms you need the following code to dispatch back to the ui thread:
public void UpdateUI(object parameter)
{
if (this.InvokeRequired)
{
Dispatcher.BeginInvoke(new Action(() => UpdateUI(parameter)));
return;
}
// Update or access here
}
In WPF the following snipped allows you to change the ui form a different thread:
public void UpdateUI(object parameter)
{
if (!Dispatcher.CheckAccess())
{
Dispatcher.BeginInvoke(new Action(() => UpdateUI(parameter)));
return;
}
// Do update or access here
}
The important thing to notice here, is that those functions will be executed on the UI thread and not on the calling thread.
Edit: the object parameter is completely optional. It was intended as a example on how to use this method with a function with parameters.

Related

How can I get the UI SynchronizationContext from a non UI thread and without forms or any object created by UI

I'm on a non UI thread and I need to create and display a SaveDialog. But when I try to Show it: .ShowDialog() I'm getting:
"An unhandled exception of type
'System.Threading.ThreadStateException' occurred in
System.Windows.Forms.dll"
I have no object created at the UI, so how can I invoke this SaveDialog at the UI thread? Is there a global UI SynchronizationContext that I can use?
EDIT: I know how to do it if I have a form/control object to call the invoke/begininvoke. Or use the SynchronizationContext.Current reference at the UI thread. But I don't have any of this.
In .NET, you would use the SynchronizationContext class and pass an instance of it to your method, maybe somewhat like this.
public void DoSomeStuffOnBackgroundThread(SynchronizationContext synchronizationContext)
{
// Do some stuff here
// ...
// Show the dialog on the UI thread
var dialog = new SaveFileDialog();
synchronizationContext.Send(() => dialog.Show());
// Send is performed synchronously, thus this line of code only executes when the dialog was closed. You can extract the file name here
var fileName = dialog.FileName;
}
The synchronization context is built when the windows forms framework is started. Thus when your application and the UI message loop has started up, simply call SynchronizationContext.Current on the UI thread to obtain the instance that you can pass to methods or objects.
Finally, I definitely recommend to encapsulate this functionality behind an interface and one class that implements this interface gets a reference to the synchronization context. Thus your production code is not poluted with these unnecessary details like thread affinity.
If you have questions, please feel free to leave a comment.
The way that I handle this is by passing the UI thread's TaskScheduler into the object that's going to be running on a background thread. Then, anytime the background thread needs to make something happen on the UI thread, I use Task.Factory.StartNew() using the TaskScheduler that I gave to the object. To illustrate:
using System.Threading.Tasks;
public class BackgroundThread {
IView _view;
TaskScheduler _uiThreadScheduler;
// This object is constructed on the main thread. The main thread's TaskScheduler
// can be acquired with TaskScheduler.FromCurrentSynchronizationContext()
public BackgroundThread(IView view, TaskScheduler uiThreadScheduler){
this._view = view;
this._uiThreadScheduler = uiThreadScheduler;
}
public void DoWork(){
// Code in this function executes on a background thread
//...
string filename;
var task = Task.Factory.StartNew(() =>
{
filename = _view.GetSaveFilename();
}, CancellationToken.None, TaskCreationOptions.None, _uiThreadScheduler);
task.Wait();
// filename now has input from the user, or is null
}
}
By hiding the UI's implementation behind the IView interface, the background thread is not dependent on any particular UI implementation.

Invoke(Delegate)

Can anybody please explain this statement written on this link
Invoke(Delegate):
Executes the specified delegate on the thread that owns the control's underlying window handle.
Can anybody explain what this means (especially the bold one) I am not able to get it clearly
The answer to this question lies in how C# Controls work
Controls in Windows Forms are bound to a specific thread and are not
thread safe. Therefore, if you are calling a control's method from a
different thread, you must use one of the control's invoke methods to
marshal the call to the proper thread. This property can be used to
determine if you must call an invoke method, which can be useful if
you do not know what thread owns a control.
From Control.InvokeRequired
Effectively, what Invoke does is ensure that the code you are calling occurs on the thread that the control "lives on" effectively preventing cross threaded exceptions.
From a historical perspective, in .Net 1.1, this was actually allowed. What it meant is that you could try and execute code on the "GUI" thread from any background thread and this would mostly work. Sometimes it would just cause your app to exit because you were effectively interrupting the GUI thread while it was doing something else. This is the Cross Threaded Exception - imagine trying to update a TextBox while the GUI is painting something else.
Which action takes priority?
Is it even possible for both to happen at once?
What happens to all of the other commands the GUI needs to run?
Effectively, you are interrupting a queue, which can have lots of unforeseen consequences. Invoke is effectively the "polite" way of getting what you want to do into that queue, and this rule was enforced from .Net 2.0 onward via a thrown InvalidOperationException.
To understand what is actually going on behind the scenes, and what is meant by "GUI Thread", it's useful to understand what a Message Pump or Message Loop is.
This is actually already answered in the question "What is a Message Pump" and is recommended reading for understanding the actual mechanism that you are tying into when interacting with controls.
Other reading you may find useful includes:
What's up with Begin Invoke
One of the cardinal rules of Windows GUI programming is that only the
thread that created a control can access and/or modify its contents
(except for a few documented exceptions). Try doing it from any other
thread and you'll get unpredictable behavior ranging from deadlock, to
exceptions to a half updated UI. The right way then to update a
control from another thread is to post an appropriate message to the
application message queue. When the message pump gets around to
executing that message, the control will get updated, on the same
thread that created it (remember, the message pump runs on the main
thread).
and, for a more code heavy overview with a representative sample:
Invalid Cross-thread Operations
// the canonical form (C# consumer)
public delegate void ControlStringConsumer(Control control, string text); // defines a delegate type
public void SetText(Control control, string text) {
if (control.InvokeRequired) {
control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text}); // invoking itself
} else {
control.Text=text; // the "functional part", executing only on the main thread
}
}
Once you have an appreciation for InvokeRequired, you may wish to consider using an extension method for wrapping these calls up. This is ably covered in the Stack Overflow question Cleaning Up Code Littered with Invoke Required.
There is also a further write up of what happened historically that may be of interest.
A control or window object in Windows Forms is just a wrapper around a Win32 window identified by a handle (sometimes called HWND). Most things you do with the control will eventually result in a Win32 API call that uses this handle. The handle is owned by the thread that created it (typically the main thread), and shouldn't be manipulated by another thread. If for some reason you need to do something with the control from another thread, you can use Invoke to ask the main thread to do it on your behalf.
For instance, if you want to change the text of a label from a worker thread, you can do something like this:
theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
If you want to modify a control it must be done in the thread in which the control was created. This Invoke method allows you to execute methods in the associated thread (the thread that owns the control's underlying window handle).
In below sample thread1 throws an exception because SetText1 is trying to modify textBox1.Text from another thread. But in thread2, Action in SetText2 is executed in the thread in which the TextBox was created
private void btn_Click(object sender, EvenetArgs e)
{
var thread1 = new Thread(SetText1);
var thread2 = new Thread(SetText2);
thread1.Start();
thread2.Start();
}
private void SetText1()
{
textBox1.Text = "Test";
}
private void SetText2()
{
textBox1.Invoke(new Action(() => textBox1.Text = "Test"));
}
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });
In practical terms it means that the delegate is guaranteed to be invoked on the main thread. This is important because in the case of windows controls if you don't update their properties on the main thread then you either don't see the change, or the control raises an exception.
The pattern is:
void OnEvent(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(() => this.OnEvent(sender, e);
return;
}
// do stuff (now you know you are on the main thread)
}
this.Invoke(delegate) make sure that you are calling the delegate the argument to this.Invoke() on main thread/created thread.
I can say a Thumb rule don't access your form controls except from main thread.
May be the following lines make sense for using Invoke()
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;
}
}
There are situations though you create a Threadpool thread(i.e worker thread) it will run on main thread. It won't create a new thread coz main thread is available for processing further instructions. So First investigate whether the current running thread is main thread using this.InvokeRequired if returns true the current code is running on worker thread so call
this.Invoke(d, new object[] { text });
else directly update the UI control(Here you are guaranteed that you are running the code on main thread.)
It means that the delegate will run on the UI thread, even if you call that method from a background worker or thread-pool thread. UI elements have thread affinity - they only like talking directly to one thread: the UI thread. The UI thread is defined as the thread that created the control instance, and is therefore associated with the window handle. But all of that is an implementation detail.
The key point is: you would call this method from a worker thread so that you can access the UI (to change the value in a label, etc) - since you are not allowed to do that from any other thread than the UI thread.
Delegate are essentially inline Action's or Func<T>. You can declare a delegate outside the scope of a method which you are running or using a lambda expression(=>); because you run the delegate within a method, you run it on the thread which is being run for the current window/application which is the bit in bold.
Lambda example
int AddFiveToNumber(int number)
{
var d = (int i => i + 5);
d.Invoke(number);
}
It means that the delegate you pass is executed on the thread that created the Control object (which is the UI thread).
You need to call this method when your application is multi-threaded and you want do some UI operation from a thread other than the UI thread, because if you just try to call a method on a Control from a different thread you'll get a System.InvalidOperationException.

Creating WPF control from a separate thread

I have an asynchronously running Task that fires an event when it's completed like this:
task.ContinueWith(() => {
if (MyEvent != null)
MyEvent(this, EventArgs.Empty);
}
The event handler then should create an instance of a WPF control. But when I try to do so, it causes an exception: The calling thread must be STA, because many UI components require this. Exception occurs in the class constructor, when calling method InitializeComponent().
As far as I know, usually accessing WPF controls from separate threads is handled using the Dispatcher.Invoke, and it always worked for me, so I tried it:
Dispatcher.Invoke(new Action(() =>
{
InitializeComponent();
}));
But in that case exception keeps occurring. How do I create an instance of a WPF control from a separate thread?
Or maybe it will be a better approach to marshal the completion event to the main UI thread. If yes, how can I do that?
You have to use a Dispatcher instance, which was associated with the UI thread. If you are writing something like this:
Dispatcher.Invoke(new Action(() =>
{
InitializeComponent();
}));
In the task body, you're using dispatcher of the calling thread, which can be a background thread from a pool.
Anyway, with tasks you shouldn't use Dispatcher directly. Use an appropriate task scheduler:
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.ContinueWhenAll(tasks.ToArray(),
result =>
{
// Put you UI calls here
}, CancellationToken.None, TaskContinuationOptions.None, ui);
where tasks is a sequence of tasks being executed with the default scheduler.
Calling InitializeComponent from the constructor on another thread seems like looking for trouble. The object isn't there yet (we're in the constructor)
Marshaling it back to the UI thread would normally do the trick but during the constructor looks like a bad idea to me.
If you want to initialize the control asynchronously just subscribe to the loaded event, so you know the object is there, spawn a thread that does some calculations/data retrieval and marshals the data back to the UI thread to display it.
I have done this in the past:
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(
delegate()
{
// Access control created by main thread
textBlock.Text = msg;
}
));

Dispatcher.Dispatch on the UI thread

I have the doubt regarding when to use the Dispatcher.Invoke to update something on UI from different Thread.
Here's my code...
public Window4()
{
InitializeComponent();
this.DataContext = this;
Task.Factory.StartNew(() => Test() );
}
private List<string> listOfString = new List<string>();
public List<string> ListOfString
{
get { return listOfString; }
set { listOfString = value; }
}
public void Test()
{
listOfString.Add("abc");
listOfString.Add("abc");
listOfString.Add("abc");
}
<Grid>
<ListView ItemsSource="{Binding ListOfString}" />
</Grid>
I am starting a new Task on the different Thread, do i need to use Dispatcher.BeginInvoke to update the UI.
In this case it is updating the UI, but i've seen some scenarios where people update UI using Dispatcher.Invoke or BeginInvoke from the different Thread.
So my question is when we have to do that and why in this case it is working fine.
Thanks & Regards,
BHavik
I have the doubt regarding when to use the Dispatcher.Invoke to update
something on UI from different Thread.
When you are on a different thread you will always have to use the dispatcher to update a ui component that belongs to another thread.
I am starting a new Task on the different Thread, do i need to use
Dispatcher.BeginInvoke to update the UI.
Tasks allow for multiple operations to be performed without blocking the thread they are called from but that doesn't mean they are on a different thread. However when updating the UI from inside a Task you will need to use the dispatcher.
In this case it is updating the UI, but i've seen some scenarios where
people update UI using Dispatcher.Invoke or BeginInvoke from the
different Thread.
Invoke will block the calling thread while it is performing the action and BeginInvoke will not. BeginInvoke will return control immediately to the caller, Invoke may cause the calling thread to hang if it is performing a heavy operation.
This is from msdn documentation,
In WPF, only the thread that created a DispatcherObject may access
that object. For example, a background thread that is spun off from
the main UI thread cannot update the contents of a Button that was
created on the UI thread. In order for the background thread to access
the Content property of the Button, the background thread must
delegate the work to the Dispatcher associated with the UI thread.
This is accomplished by using either Invoke or BeginInvoke. Invoke is
synchronous and BeginInvoke is asynchronous.
Edit: In response to your comment I ran some tests.
When calling Test() from a task (without using the dispatcher) I got this error "The calling thread cannot access this object because a different thread owns it."
So I created a method called PrintThreadID(). I printed the thread before entering the task then from inside the task and it does report both are running on the same thread ID.
The error is misleading because it says the calling thread is different than the one that owns it which the PrintThreadID() function shows is not true, they are in fact on the same thread. Tasks while on the same thread still cannot update a UI component without using Dispather.Invoke().
So here is a working example which will update the Grid from a task.
public partial class MainWindow : Window
{
public List<string> myList { get; private set; }
public MainWindow()
{
InitializeComponent();
myList = new List<string>();
label1.Content = Thread.CurrentThread.ManagedThreadId.ToString();
Task.Factory.StartNew(PrintThreadID);
Task.Factory.StartNew(Test);
}
private void PrintThreadID()
{
label1.Dispatcher.Invoke(new Action(() =>
label1.Content += "..." + Thread.CurrentThread.ManagedThreadId.ToString()));
}
private void Test()
{
myList.Add("abc");
myList.Add("abc");
myList.Add("abc");
// if you do not use the dispatcher you will get the error "The calling thread cannot access this object because a different thread owns it."
dataGrid1.Dispatcher.Invoke(new Action(() =>
{
dataGrid1.ItemsSource = myList.Select(i => new { Item = i });
}));
}
}
Your test isn't valid as it isn't actually updating your UI. If you want proof, add this sleep call:
public void Test()
{
Thread.Sleep(10000);
listOfString.Add("abc");
listOfString.Add("abc");
listOfString.Add("abc");
}
You'll find that your UI appears and the list is empty. 10 seconds, 30 seconds, 3 months later, the list won't contain your strings.
Instead your test is demonstrating a race condition - your Test() method is completing fast enough that the strings are added to the list before the UI appears on screen and reads the list.
To fix it, change your collection to an ObservableCollection<string>. But then you'll encounter the next problem - you can't update an ObservableCollection on a background thread. So that's where the Dispatcher comes in.

Background Worker events

pseudo code:
form1
{
int i;
label1;
Add()
{
i++;
label1 = i.ToString(); //#ErrorLine
}
backgroundworker worker;
worker_DoWork()
{
FileGuard guard = new FileGuard();
guard.FileKilled += guard.KillH(Add);
guard.StarGuarding(); //there is system watcher inside
//this guard and some processing code
//that will fire event FileKilled();
}
}
Afer calling StartGuarding() worker will be compleated
But when there is event FileKilled fired I goth this error on line #ErrorLine
Cross-thread operation not valid: Control 'form1' accessed from a thread other than the thread it was created on.
This has nothing to do with the events themselves, but rather the fact that you are accessing UI controls from another thread. In Windows Forms, you are not allowed to interact with the UI from any other thread than the main UI thread.
You can use InvokeRequired to check whether you are on a thread that has no access to the UI, and then use Invoke to run code on the UI thread if required. It might look something like this:
private void DoStuffWithGUI()
{
if (InvokeRequired)
{
Action work = DoStuffWithGUI;
Invoke(work);
}
else
{
// Your normal logic
}
}
You can use the UI directly from ProgressChanged and RunWorkerCompletedEvents (because they are automatically marshalled to the UI thread). But all work you do within DoWork, (and therefore all events you might raise as part of the work) runs in a separate thread, and must be marshalled to the UI thread using Invoke. From MSDN for BackgroundWorker:
You must be careful not to manipulate
any user-interface objects in your
DoWork event handler. Instead,
communicate to the user interface
through the ProgressChanged and
RunWorkerCompleted events.
It's because a third thread is used when FileKilled is invoked by the system.
As for BackgroundWorker you should use events to handle GUI updates: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
You cannot access Windows Forms or WPF objects from anything other than the form they were created on, hence your issue.
Use a dispatcher to send the update back to your UI Thread.
If you can give detail on whether you're using WinForms or WPF we can give more information.
You cannot change modify the controls from a thread other than on which they were created. You need to use the InvokeRequired property and Invoke method to marshal the calls to the UI thread from the background thread.
private readonly _lockObject = new Object();
Add()
{
lock(_lockObject)
{
i++;
if(label1.InvokeRequired)
Invoke(new Action( () => label1 = i.ToString()));
else
label1 = i.ToString();
}
}
Note that the lock is not necessary to avoid this exception. It added to make the method thread safe.

Categories