In my main window (Thread A), I start a new thread (Thread B) which does some work while the user waits.
Thread B fires events if there is an error or extra information is required from the user, Thread A will listen to these events.
In Thread A's event listener I need to show dialog messages to the user, I have a custom dialog window and show it using dialogWindow.showDialog(). This works fine, but causes an error when I try and set the owner of the dialog, I do this dialogWindow.Owner = Window.GetWindow(this).
The error that I get is: The calling thread cannot access this object because a different thread owns it.
What is the correct way to listen for events that are fired from a different thread?
The event listener code will run implicitly at the thread which fires the event, so the event listener is not thread-bound.
If you want to show something in the UI as a result of event handling, you should do the marshalling yourself. Something like that:
void OnEvent(object sender, EventArgs args)
{
// runs in the event sender's thread
string x = ComputeChanges(args);
Dispatcher.BeginInvoke((Action)(
() => UpdateUI(x)
));
}
void UpdateUI(string x)
{
// runs in the UI thread
control.Content = x;
// - or -
new DialogWindow() { Content = x, Owner = this }.ShowDialog();
// - or whatever
}
So: you execute your computations (if any) preferrably in the background thread, without touching the UI; after that, when you know which are the needed changes in the UI, you execute the UI-updating code in the UI thread.
The Dispatcher is a property of a control, so if your code is a part of UI, you'll have your Dispatcher for free. Otherwise, you can take the dispatcher from any of the controls (e.g., control.Dispatcher).
The correct way to raise an event to the UI thread from the background thread is that, the event should be raised on that Dispatcher,
Key here is get the dispatcher of UIthread before hand.
UIDisaptcher.BeginInvoke((ThreadStart)(() => RaiseEventToUIThread()));
when the UI thread listens to the raised event it can set the Owner property(as the window was created by the UI thread).
Sure -> what we do is to use the SynchronizationContext.
So when you start a new thread, you capture (on UI thread) the current context and pass it into the second thread as parameter.
Then on the second thread when you want to raise an event you do it this way:
if (_uiThreadId != Thread.CurrentThread.ManagedThreadId)
{
_uiContext.Post(
new SendOrPostCallback(delegate(object o) { OnYourEvent((EventArgs)o); }),
e);
}
else
OnYourEvent(e);
Related
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;
}
));
I've got an [STAThread] main that contains something like this:
try
{
// Start the port enumerator.
Enumerator enumerator = new Enumerator();
// Display the main window and start everything going.
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TopViewForm(ref enumerator));
}
catch (Exception)
{
}
The enumerator object itself contains a background worker thread. The background work is initialised like this:
MyBackgroundWorker = new BackgroundWorker();
MyBackgroundWorker.WorkerSupportsCancellation = true;
MyBackgroundWorker.WorkerReportsProgress = true;
MyBackgroundWorker.DoWork +=new DoWorkEventHandler(MyBackgroundWorker_DoWork);
MyBackgroundWorker.ProgressChanged += new ProgressChangedEventHandler(MyBackgroundWorker_ProgressChanged);
MyBackgroundWorker.RunWorkerCompleted +=new RunWorkerCompletedEventHandler(MyBackgroundWorker_RunWorkerCompleted);
MyBackgroundWorker.RunWorkerAsync();
Now, in order to report progress back to the main form (the instance of TopViewForm), the thread in Enumerator is calling ReportProgress with some appropriate user object. ReportProgress, I assume, will arrive in the form of ProgressChanged event and be executed on the main UI thread, which is the thread the Enumerator itself was created on (i.e. not the BackgroundWorker's thread).
Given that this is the case, how is it possible that I'm getting a cross-thread violation error when I try to set a node in a TreeView on the main process thread in response to an event from this object? The ProgressChanged categorically does not appear to be run on the main process thread, looking at the call stack.
I assume then, that Application.Run generates a new thread and runs the form with that? But the documentation says otherwise. Either I've made a dumb error or there's something I don't understand here.
Does anyone have any thoughts?
I believe that the progress changed event is running before Application.Run is first called. If you don't start the background worker from Main, but rather from inside one of the forms you will be sure that the message loop already exists when the background worker is created and starts reporting progress.
BackgroundWorker relies on a current SynchronizationContext being set in order to function. ProgressChanged event is implemented as SendOrPostCallback Delegate and as it stated in msdn documantation SendOrPostCallback represents a callback method that you want to execute when a message is to be dispatched to a synchronization context. Adding BackgroundWorker to a form in a Form designer sets SynchronizationContext properly. In your case BackgroundWorker has no SynchronizationContext assigned.
When does InvalidAsynchronousStateException get thrown?
I have the following piece of code:
control.InvokeRequired ?
control.Invoke(expression) :
expression();
In some random cases I get InvalidAsynchronousStateException and my application hangs, after doing some reading it seems to be that this exception will be thrown when the thread where the control was created finished. Is this correct? If so, this doesn't seem to be the case, unless something is making my application crash and this exception is just a consequence? is this possible?
System.ComponentModel.InvalidAsynchronousStateException: An error occurred invoking the method. The destination thread no longer exists.
at System.Windows.Forms.Control.WaitForWaitHandle(WaitHandle waitHandle)
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.Invoke(Delegate method)
at Optimus.Desktop.Framework.Spring.Aspects.UIThreadInterceptor.Invoke(IMethodInvocation invocation) in c:\Optimus\Desktop\Framework\Spring\Aspects\UIThreadInterceptor.cs:line 22
at Spring.Aop.Framework.AbstractMethodInvocation.Proceed()
at Spring.Aop.Framework.DynamicProxy.AdvisedProxy.Invoke(Object proxy, Object target, Type targetType, MethodInfo targetMethod, MethodInfo proxyMethod, Object[] args, IList interceptors)
at InheritanceAopProxy_4fda07e8828744839065a154b30915ee.Dispose(Boolean disposing)
at System.ComponentModel.Component.Finalize()
btw, I've checked this answer and didn't clarify my doubt -> InvalidAsynchronousStateException in function that checks if invoke is required for control
Usually this occurs when a background thread is attempting to invoke to a UI thread after the UI thread has already exited. Do you by any chance attempt to run different forms each in their own thread, or do you Show() forms from a non-UI thread, or Invoke() to a form before it is shown?
The background is as follows:
1) Every control (including Forms) has a handle. This is used to tie the control back to the underlying windows GDI objects.
2) The control's handle is usually not created when the control itself is created. The handle is created when the control is Show()n for the first time.
3) When Invoking to a control, the .NET API attempts to locate the control's UI thread using it's handle. If the form has not yet been shown, the CURRENT THREAD (the invoking thread) will be assigned as the UI thread.
4) The UI thread for a control is expected to run a message loop for handling that control (which happens automatically when you do, for instance, Application.Run(someForm);
5) So the common mistake is that you create a form F, Invoke() or BeginInvoke() to it from a temporary or threadpool thread, which creates the form's handle and is therefore assigned as the form's UI thread. Then the background thread exits, or is terminated by the threadpool, or simply fails to run a message loop, since it is not aware that it has been designated a UI thread. Subsequently, any invocations to that form fail with this exception. The exception is thrown simply because the form's assigned 'UI thread' is not running a message loop.
See Ivan's post for a detailed analysis of how this happens: http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html
I've been faced to the same issue recently. My form contains several invisible user controls that may be required to appear later in the life cycle of the application. Sometimes, those requests come from background threads.
The problem was that even if I enclose control.Visible = true inside a control.Invoke, the control was actually assigned to the background thread (as mentioned in Chris's point #3) instead of the form's main UI thread. A simple workaround for me was to call once the IWin32Window.Handle property during the creation of the parent form (for instance from the form's Load event) This ensures that the control is created in main UI thread without making it visible.
public partial class MyForm : Form
{
private void MyForm_Load(object sender, EventArgs e)
{
ForceControlCreation(control1);
ForceControlCreation(control2);
}
private void ForceControlCreation(IWin32Window control)
{
// Ensures that the subject control is created in the same thread as the parent
// form's without making it actually visible if not required. This will prevent
// any possible InvalidAsynchronousStateException, if the control is later
// invoked first from a background thread.
var handle = control.Handle;
}
}
As others have correctly shown this happens when a UI component is disposed or is completed (return) while a different thread is still invoking code on the same UI component.
This usually happens when a user closes a window (a form) that has been updated by a different thread and this is more prevalent when the update frequency is high (because the chance of having an incomplete invoke when the user closes the form is high).
This can be gracefully handled by:
Set a flag to indicate an invoke is in progress
Intercept the UI dispose or return
Stop further (new) invokes from taking place
Wait until existing invokes finish
Complete the intended dispose or return
Below example shows how to gracefully handle the most common scenario (when a form is closed while it's been updated).
The example is from a simple form that has a listbox that is updated from an outside thread via a public method (AddItem(string)).
Flags
private bool invokeInProgress = false;
private bool stopInvoking = false
Invoking code
public void AddItem(string newItem)
{
if (listView1.InvokeRequired)
{
if (stopInvoking != true) // don't start new invokes if the flag is set
{
invokeInProgress = true; // let the form know if an invoke has started
listView1.Invoke(new Action(() => addItem(newItem))); // invoke
invokeInProgress = false; // the invoke is complete
}
return;
}
listView1.Items.Add(newItem);
listView1.Items[listView1.Items.Count - 1].EnsureVisible();
}
Intercepting and managing form closing event
private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (invokeInProgress)
{
e.Cancel = true; // cancel the original event
stopInvoking = true; // advise to stop taking new work
// now wait until current invoke finishes
await Task.Factory.StartNew(() =>
{
while (invokeInProgress);
});
// now close the form
this.Close();
}
}
You can further extend this by exposing a method or a property that lets the users (other threads) know that the form is shutting down so that the callers can gracefully handle the situation.
Example below shows how a new property (ShuttingDown) allows a caller to handle its flow correctly if a user closes the display form.
New flag on form
public bool ShuttingDown { get { return stopInvoking; } }
Caller now can detect the problem
static void Main()
{
Form1 frm = new Form1();
Task.Factory.StartNew(() => frm.ShowDialog(), TaskCreationOptions.LongRunning);
int i = 0;
while (i < 2000)
{
if (frm.ShuttingDown != true) // the clients can also be notified and allowed to handle the UI disruption
{
frm.addItem(Guid.NewGuid().ToString());
}
else
{
MessageBox.Show("Form is closing. Stopping the process.");
break;
}
i++;
}
MessageBox.Show("Program completed! i=" + i.ToString());
}
You can read more and download the sample project from here: http://www.ilearnttoday.com/c-sharp-the-destination-thread-no-longer-exists
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.
I am working with a framework that runs its own event dispatcher in a separate thread. The framework may generate some events.
class SomeDataSource {
public event OnFrameworkEvent;
void FrameworkCallback() {
// This function runs on framework's thread.
if (OnFrameworkEvent != null)
OnFrameworkEvent(args);
}
}
I want to deliver these events to a Winforms object on Winforms thread. I obviously check for InvokeRequired and dispatch it to Winforms thread if necessary.
class SomeForm : Form {
// ...
public void SomeAction(SomeArgs args) {
if (InvokeRequired) {
BeginInvoke(new Action(SomeAction), args);
return;
}
// ...
}
}
Now events may be delivered when the form is in the process of being closed, which causes all sorts of problems, so I unregister the form's event handler from framework's event source on Winforms thread like this:
var form = new SomeForm();
var src = new SomeDataSource();
// ...
src.OnFrameworkEvent += form.SomeAction;
form.Closing += (sender, eargs) => src.OnFrameworkEvent -= form.SomeAction;
Now, is this approach thread-safe? If the form is in the process of being closed, and a foreign thread calls BeginInvoke, will the invocation still be queued for execution if the form is closed? (which means I still have a chance of encountering the same problem)
Is there a better approach or recommended pattern for cross-thread event handling?
No it is not. The thread might just be executing the event handler while you unregister it and close the form. Small odds, but not zero. You have to have the thread stopped before you can close the form. If you don't want to abort it, you'll have to keep the form open by canceling the FormClosing event, then let the thread's completion callback close the form.
Check this thread for more info.
You can add this code to constructor CheckForIllegalCrossThreadCalls = false; and no exception will be thrown.
I haven't used a framework with its own event dispatcher, but I had my own experience with the threads that I created. Here's my experience
This approach is not thread-safe. The invocation will still be called even if the program itself is closed. I saw this in task manager (after the program is closed as you say) as hanging threads. (even if you kill the program from task manager also.). I had to kill those threads seperately later.
When the form is closing you have to kill the dispatcher thread so that it does not hang if anything wrong happens in that thread.
form.Closing += (sender, eargs) => src.OnFrameworkEvent -= form.SomeAction;
// pseudo-code (find c# equivalent)
if (dispatcherthread.isrunning)
dispatcherThread.kill();