Dispatcher and RaiseCanExecuteChanged() blocking [duplicate] - c#

I'm building a chat client and am not 100% sure on how to use the dispatcher. So the question is I have a method as such:
public void LostConnection()
{
myGUI.chatBox.AppendText("Lost connection to room: "+ myGUI.UsernameText.ToString() + "\r\n");
}
Do i need to surrond the statement within (myGUI.chatBox... ) with a Dispatcher.Invoke? I appreciate any help.

Your app has a main UI thread (usually ManagedThreadId==1). Typically in a chat app your events will come in on other threads (either dedicated socket listen threads or thread pool threads from listening code). If you want to update the UI from an event that gets pull on some other thread you must use the dispatcher. A useful test here is the Dispatcher.CheckAccess() method that returns true if code is on UI thread and false if on some other thread. A typical call looks something like:
using System.Windows.Threading; // For Dispatcher.
if (Application.Current.Dispatcher.CheckAccess()) {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}
else {
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(()=>{
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
}));
}
If you're in the main window you can use:
Dispatcher.BeginInvoke(...
If you're in someother context eg a view model then use:
Application.Current.Dispatcher.BeginInvoke(
Invoke vs BeginInvoke
Use Invoke if you want the current thread to wait until the UI thread has processed the dispatch code or BeginInvoke if you want current thread to continue without waiting for operation to complete on UI thread.
MessageBox, Dispatchers and Invoke/BeginInvoke:
Dispatcher.Invoke will block your thread until the MessageBox is dismissed.
Dispatcher.BeginInvoke will allow your thread code to continue to execute while the UI thread will block on the MessageBox call until its dismissed.
CurrentDispatcher vs Current.Dispatcher!
Be ware of Dispatcher.CurrentDispatcher as my understanding of this is that is will return a Dispatcher for the current thread not the UI thread. Generally are you interested in the dispatcher on the UI thread - Application.Current.Dispatcher always returns this.
Additional note:
If you are finding you are having to check dispatcher CheckAccess often then a useful helper method is:
public void DispatchIfNecessary(Action action) {
if (!Dispatcher.CheckAccess())
Dispatcher.Invoke(action);
else
action.Invoke();
}
Which can be called as:
DispatchIfNecessary(() => {
network_links.Add(new NetworkLinkVM(link, start_node, end_node));
});

I had problems with Application.Current.Dispatcher.BeginInvoke and the object.Invoke() methods.
This worked for me:
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
// code...
}));

Something like this (off the top of my head) should work:
public void LostConnection()
{
myGUI.Invoke
((MethodInvoker)delegate
{
myGUI.chatBox.AppendText("Lost connection to room: "+ myGUI.UsernameText.ToString() + "\r\n");
});
}

Related

How to dispatch to main thread?

I would like to execute some code from a non-main thread inside the main thread (UI thread) in .Net 6 with C#.
I've tried to use this code:
await Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { }
);
This doesn't work, since Windows.UI.Core.CoreWindow.GetForCurrentThread() returns null.
My second try was:
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { }
);
This fails, because Windows.ApplicationModel.Core.CoreApplication.MainView throws a System.InvalidOperationException.
Another way should be:
await System.Windows.Threading.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { }
);
But the System.Windows.Threading namespace is not available for me, since I'm using .Net 6 and it's not longer supported in it.
Any idea, how I can execute some code from a non-main thread inside the main-thread (UI thread)?
I would like to execute some code from a non-main thread inside the main thread (UI thread) in .Net 6 with C#.
I strongly recommend that you don't. It's far cleaner to have your async methods use something like IProgress<T> to indirectly update the UI as necessary. If you structure your code so that the main thread calls the background threads instead of the background threads manipulating the UI through the UI thread, then you'll end up with a much cleaner design where your logic is less tied to your UI controls.
That said, if you really want to, then the solution is to capture the dispatcher on the UI thread before the background work begins, and have the background work use that dispatcher (not the "current dispatcher") when posting work to the UI thread.
SynchronizationContext is a good solution to switch to the main thread. But it's not implemented for all .Net app types.
For example, for a console app, there is no solution implemented.
But for Windows Forms, WindowsFormsSynchronizationContext works fine.
private SynchronizationContext _synchronizationContext;
Initialization inside, called inside the main thread:
_synchronizationContext = new WindowsFormsSynchronizationContext();
After this, you can call from a different thread:
SynchronizationContext.SetSynchronizationContext(_synchronizationContext);
... here we are in a separate thread
_synchronizationContext.Post(
(state) => {
... this will be executed in the main thread
},
null);
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync
(CoreDispatcherPriority.Normal, () =>
{
do something on UI thread
});

How to marshall work onto the main thread using TPL tasks in C# without causing a deadlock?

I am writing a library that is consuming a resource and for whatever reason the API was designed in a way that events will be raised on different threads but calls of the API has to be done on the main thread.
Let's say the API that I am trying to consume is defined as (I am going to omit event definitions):
public sealed class DodgyService
{
public void MethodThatHasToBeCalledOnTheMainThread() { ... }
}
To consume this API I have added a service on my library called Service (Yup, very original name) that will create a new task (that will run on the main thread as I am specifying a TaskScheduler that has been created from the SynchronizationContext).
Here is my implementation:
public class Service
{
private readonly TaskFactory _taskFactory;
private readonly TaskScheduler _mainThreadScheduler;
public Service(TaskFactory taskFactory, TaskScheduler mainThreadScheduler)
{
_taskFactory = taskFactory;
_mainThreadScheduler = mainThreadScheduler;
}
// Assume this method can be called from any thread.
// In this sample is called by the main thread but most of the time
// the caller will be running on a background thread.
public Task ExecuteAsync(string taskName)
{
return _taskFactory.StartNew(
() => ReallyLongCallThatForWhateverStupidReasonHasToBeCalledOnMainThread(taskName),
new CancellationToken(false), TaskCreationOptions.None, _mainThreadScheduler)
.ContinueWith(task => Trace.TraceInformation("ExecuteAsync has completed on \"{0}\"...", taskName));
}
private void ReallyLongCallThatForWhateverStupidReasonHasToBeCalledOnMainThread(string taskName)
{
Trace.TraceInformation("Starting \"{0}\" really long call...", taskName);
new DodgyService().MethodThatHasToBeCalledOnTheMainThread();
Trace.TraceInformation("Finished \"{0}\" really long call...", taskName);
}
}
Now, if I perform the call of my service (on the main thread) and try to wait on the main thread the application enters a deadlock as the main thread will be waiting for the tasks that has been scheduled to execute on the main thread.
How do I marshall these calls onto the main thread without blocking the entire process?
At some point I thought on performing the detection of the main thread before creating the new task but I don't want to hack this.
For anybody interested, I got a gist here with the code and a WPF app that exhibits the issue.
On btw, the library has to be written on .net framework 4.0
Edit!
I solved my issue following the advice provided by Scott Chamberlain as provided here
as the main thread will be waiting for the tasks
That's a guaranteed deadlock. A task cannot execute on the main thread until it is idle, running the dispatcher loop (aka pumping the message loop). It is that dispatcher loop that implements the magic of getting code to run on a specific thread. The main thread however won't be idle, it is "waiting for the tasks". So the task cannot complete because the main thread won't go idle, the main thread cannot go idle because the task won't complete. Deadlock city.
You must rewrite the code so your main thread won't wait. Move whatever code that appears after the wait call to another task that runs on the main thread, just like that ReallyLongCall().
Do note that you don't seem to get any mileage at all from using tasks, your snippet suggests that none of the code that matters runs on a worker thread. So you might as well call it directly, solves the problem as well.
From your example program:
private void HandleClosed(object sender, EventArgs e)
{
var list = new[]
{
_service.ExecuteAsync("first task"),
_service.ExecuteAsync("second task"),
_service.ExecuteAsync("third task")
};
//uncommenting this line blocks all three previous activities as expected
//as it drives the current main thread to wait for other tasks waiting to be executed by the main thread.
//Task.WaitAll(list);
}
Task.WaitAll is a blocking call, you can't perform blocking calls on the main thread or you will cause deadlocks. What you can do (if you are using Visual Studio 2012 or newer) is use the NuGet package Microsoft.Bcl.Async which gives async/await support to .Net 4.0.
After adding the package change the code to
private async void HandleClosed(object sender, EventArgs e)
{
var list = new[]
{
_service.ExecuteAsync("first task"),
_service.ExecuteAsync("second task"),
_service.ExecuteAsync("third task")
};
//uncommenting this line blocks all three previous activities as expected
//as it drives the current main thread to wait for other tasks waiting to be executed by the main thread.
await TaskEx.WhenAll(list);
}
and your program will no-longer deadlock (it also does not execute any code after await TaskEx.WhenAll(list); but that is because this code is running during the shutdown process and when you await it lets the shutdown continue on processing, if it was placed elsewhere like a click event you would see more normal behavior).
Another option is have a 2nd "Main Thread" and dispatch the work to that. Often when something must be run on "the main" thread are actually saying they require to be run on "a STA Windows Message pumped that the object was initially created on" thread. Here is a example how to to it (taken from here)
private void runBrowserThread(Uri url) {
var th = new Thread(() => {
var br = new WebBrowser();
br.DocumentCompleted += browser_DocumentCompleted;
br.Navigate(url);
Application.Run();
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
var br = sender as WebBrowser;
if (br.Url == e.Url) {
Console.WriteLine("Natigated to {0}", e.Url);
Application.ExitThread(); // Stops the thread
}
}
#HansPassant is correct; by blocking the dispatcher thread to wait on the tasks, you prevent the tasks from ever being executed. The simplest change you could probably make would be to replace Task.WaitAll(list) with:
_taskFactory.ContinueWhenAll(
list,
tasks => { /* resume here */ });
...and then move any code which followed the call to WaitAll() into the continuation. Remember to check the task results and respond appropriately to any exceptions that might have occurred.
But unless there is some tangible benefit to using Tasks that is not apparent in your example code, I would heed Hans' advice and simply forego the Tasks in favor of synchronous calls.

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;
}
));

Problem with the code while calling Invoke() on form?

I have a method which is called on a different thread than UI thread.
When this method is called the control is gone , I mean nothing happens.
The code is like below:
private void MainForm_NewMeasurementState(Measurement measurement)
{
try
{
if (InvokeRequired)
{
// we were called on a worker thread
// marshall the call to the user interface thread
this.Invoke(new Action<Measurement>(MainForm_NewMeasurementState), new object[] { measurement });
return;
}
// some other code
}
The control comes in the if statement but then I don't know what happens, the other code is never called.
Any help will be appreciated.
It could be that your main thread is blocked, perhaps because it is waiting for your code to complete (i.e. your code is deadlocked because two threads are waiting for each other).
Try to find out why the main UI thread is blocked, or else use BeginInvoke instead of Invoke.

How can I tell when a thread have finished when using a thread pool?

My thread:
public void main_news_thread(MainApplication main)
{
ThreadPool.QueueUserWorkItem(p => check_news(validrsslist, 0));
}
I call this thread every interval of time...
How can I know when the thread finishes so I can call two other methods which deal with the GUI? How can I refer to this threadpool thread?
Since you are talking about UI, you might want to look at BackgroundWorker, which offers a RunWorkerCompleted event that fires on the UI thread, and indicate success/failure/cancel etc.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker_events.aspx
Personally, though, I'd just run a callback method at the end of my worker code (remembering to switch back to the UI thread, via Dispatcher.Invoke in WPF or this.Invoke in winforms).
You can execute the methods in the thread itself (you have to take care of invoking yourself to access the gui thread):
ThreadPool.QueueUserWorkItem(p => {
check_news(validrsslist, 0);
//do something after the task is finished
});

Categories