Comparing SynchronizationContext - c#

How do I compare SynchronizationContext? It seems that the same Dispatcher can create different SynchronizationContext when using BeginInvoke. When I drill down into the two (unequal) contexts, I see that the dispatcher Thread ID is the same, yet they are not Equal to each other.
public partial class MainWindow : Window
{
private SynchronizationContext contexta;
private SynchronizationContext contextb;
private SynchronizationContext contextc;
private SynchronizationContext contextd;
public MainWindow()
{
InitializeComponent();
contexta = SynchronizationContext.Current;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
contextb = SynchronizationContext.Current;
Dispatcher.Invoke(() =>
{
contextc = SynchronizationContext.Current;
});
Dispatcher.BeginInvoke(new Action(() =>
{
contextd = SynchronizationContext.Current;
}));
Debug.Assert(contexta != contextb);
Debug.Assert(contexta == contextc); // fails... why?!?!?
Debug.Assert(contexta == contextd); // fails... why?!?!?
Debug.Assert(contextc == contextd); // fails... why?!?!?
}
Maybe the two of them cannot be used together. I noticed that this actually works:
contexta.Send(new SendOrPostCallback((s) =>
{
contexte = SynchronizationContext.Current;
}), null);
Update But strangely, it doesn't always work.
public override void AddRange(IEnumerable<T> items)
{
if (SynchronizationContext.Current == _context)
{
base.AddRange(items);
}
else
{
_context.Send(new SendOrPostCallback((state) =>
{
AddRange(state as IEnumerable<T>);
}), items);
}
}
never gets a matched _context and goes on forever, for example. Even though it shouldn't. This latter example the threads actually end up being the same, and there is a context, but it is different.
Update2 Ok, I got it to work, but I really feel uncomfortable about it. Apparently, when you Post or Send, your task is run from the right thread, but if you aren't coming from the UI, it seems that a new SynchronizationContext is generated.
public override void AddRange(IEnumerable<T> items)
{
if (SynchronizationContext.Current == _context)
{
base.AddRange(items);
}
else
{
_context.Post(new SendOrPostCallback((state) =>
{
if (SynchronizationContext.Current != _context)
SynchronizationContext.SetSynchronizationContext(_context); // called every time.. strange
AddRange(items);
}), null);
}
}
And look at this:
"Requires full trust for the immediate caller. This member cannot be used by partially trusted or transparent code." :(

I think you are interested in
BaseCompatibilityPreferences.ReuseDispatcherSynchronizationContextInstance.
This setting dictates whether a single SynchronizationContext instance is used for a given Dispatcher object or not. It is true by default until .net 4, and false in .net 4.5 (this is the behavior change that LukeN observes).
Now if your goal is just to make a direct call rather than calling .Send() I'd say:
when calling .Send(), the DispatcherSynchronizationContext actually just makes a direct call if you are on the correct thread (doesn't use the dispatcher Queue) so you're not gaining much anyway (a few checks and calls from the extra layers of indirection).
if you only code against WPF, you can use Dispatcher.CheckAccess() and Dispatcher.Invoke() to do what you want.
In the general case, there is no way to "compare" two SynchronizationContexts, so you should just call .Send(). It's not likely to be a performance issue anyway, remember that premature optimization is the root of all evil -> measure first.

Related

C# - Solving potential deadlock solution

I have a LongOperationHelper that I activate on each potentially long operation.
It displays a semi transparent layer and does not allow any click until the operation ends and a spinning control to indicate progress.
It looks something like that (missing some business logic but the idea is clear I think):
Edited: (Added the missing code of the common states that actually needed the locking - this is more like the problematic code)
(My solution is posted in an answer bellow)
public static class LongOperationHelper
{
private static object _synchObject = new object();
private static Dictionary<string, int> _calls = new Dictionary<string, int>();
private static Action<string> DisplayLongOperationRequested;
private static Action<string> StopLongOperationRequested;
public static void Begin(string messageKey)
{
lock (_synchObject)
{
if (_calls.ContainsKey(messageKey))
{
_calls[messageKey]++;
}
else
{
_calls.Add(messageKey, 1);
DispatcherHelper.InvokeIfNecesary(() =>
{
//Raise event for the MainViewModel to display the long operation layer
DisplayLongOperationRequested?.Invoke(messageKey);
});
}
}
}
public static void End(string messageKey)
{
lock (_synchObject)
{
if (_calls.ContainsKey(messageKey))
{
if (_calls[messageKey] > 1)
{
_calls[messageKey]--;
}
else
{
_calls.Remove(messageKey);
DispatcherHelper.InvokeIfNecesary(() =>
{
//Raise event for the MainViewModel to stop displaying the long operation layer
StopLongOperationRequested?.Invoke(messageKey);
});
}
}
else
{
throw new Exception("Cannot End long operation that has not began");
}
}
}
}
So as you can probably see, there is a potential deadlock in there, if:
Someone calls Begin from a non UI thread.
It enters the lock
Someone calls Begin or End from a UI thread and gets locked
The first Begin call tries to Dispatch to the UI thread.
Result: Deadlock!
I want to make this Helper thread safe, so that any thread might call Begin, or End at any given time, interested to see if there is any known pattern, any Ideas?
Thanks!
Don't lock for the entire method. Lock only when you touch the fields that need it, and unlock as soon as you're done. Lock and unlock each time you touch those fields. Otherwise, you'll end up with deadlocks like this.
You can also consider using ReaderWriterLockSlim, which differentiates between read locks and write locks. It lets multiple threads read at the same time, but locks everyone out when a write lock is taken. There is an example on how to use it in that documentation.
The entire purpose of having a "UI thread" is to avoid synchronization exactly like this. The fact that all UI code needs to run on a single thread means that it, by definition, cannot run concurrently. You have no need to use locks to make your UI code run atomically because it's all running on a single thread.
Writing UI code that required the programmer to do their own locking is sufficiently hard and error prone that the whole framework was designed around the idea that it's unreasonable to expect people to do it (correctly), and that it's much easier to simply force all UI code to go into a single thread, where other synchronization mechanisms won't be needed.
Here is the "Deadlock free" code:
I have relocated the dispatching to the UI thread to outside of the lock.
(Can someone still see a potential deadlock here?)
public static class LongOperationHelper
{
private static object _synchObject = new object();
private static Dictionary<string, int> _calls = new Dictionary<string, int>();
private static Action<string> DisplayLongOperationRequested;
private static Action<string> StopLongOperationRequested;
public static void Begin(string messageKey)
{
bool isRaiseEvent = false;
lock (_synchObject)
{
if (_calls.ContainsKey(messageKey))
{
_calls[messageKey]++;
}
else
{
_calls.Add(messageKey, 1);
isRaiseEvent = true;
}
}
//This code got out of the lock, therefore cannot create a deadlock
if (isRaiseEvent)
{
DispatcherHelper.InvokeIfNecesary(() =>
{
//Raise event for the MainViewModel to display the long operation layer
DisplayLongOperationRequested?.Invoke(messageKey);
});
}
}
public static void End(string messageKey)
{
bool isRaiseEvent = false;
lock (_synchObject)
{
if (_calls.ContainsKey(messageKey))
{
if (_calls[messageKey] > 1)
{
_calls[messageKey]--;
}
else
{
_calls.Remove(messageKey);
isRaiseEvent = true;
}
}
else
{
throw new Exception("Cannot End long operation that has not began");
}
}
//This code got out of the lock, therefore cannot create a deadlock
if (isRaiseEvent)
{
DispatcherHelper.InvokeIfNecesary(() =>
{
StopLongOperationRequested?.Invoke(messageKey);
});
}
}
}

How to make correct encapsulation with multithreading .NET C#

As you can see, I have two classes. RfidReaderHardware generates event in thread "th", but Form running at another thread. As you can see, in form if use Invoke method of ListViewControl. So, question is how to change RfidReaderHardware to resolve encapsulation problem.
public class RfidReaderHardware : IDisposable
{
public event EventHandler<RfidReaderEventArgs> OnNewPackage;
Thread th;
//This method will be called from thread "th"
private void FireNewPackageEvent(UHFPackage package)
{
... code ...
}
... some code ...
}
and we have example code, where this event is using
public partial class PassageForm : Form
{
RfidReaderHardware RfidReader = new RfidReaderHardware(...);
private void Form1_Load(object sender, EventArgs e)
{
RfidReader.OnNewPackage += NewRfidPackage;
}
//not sure, but i think it's running in thread "th"
private void NewRfidPackage(Object o, RfidReaderEventArgs e)
{
ListViewItem item = new ListViewItem();
//from point of encapsulation view it's wrong as you know
CPackageList.Invoke(new Action(() => {CPackageList.Items.Add(item); }));
}
}
question is how to change RfidReaderHardware to resolve encapsulation problem
In fact there is no encapsulation problem. By definition, the relation between event source and subscriber is one to many, hence the source cannot "encapsulate" a logic for a specific subscriber. It's the subscriber choice how to handle the notification. One can ignore it, or handle it immediately, or like in your case handle it on the UI thread either synchronously (using Control.Invoke) or asynchronously (using Control.BeginInvoke).
Not so sure there's any real need to fix this, having the UI object itself deal with the fact that event is fired on the "wrong" thread is not a flaw. As long as you know it is in fact fired on the wrong thread, a documentation requirement.
.NET however has a general mechanism to solve this, it is used in several places inside the .NET Framework code. Your RfidReaderHardware class constructor can copy the value of SynchronizationContext.Current and store it in a field. With the implicit assumption that the object is created by code that runs on the UI thread. When you are ready to fire the event, and the copied object isn't null, you can then use its Post() or Send() method. Which automagically makes the code resume on the UI thread. Regardless of the specific UI class library that was used, works just as well in a WPF or Universal app for example.
Some sample code, it doesn't take much:
public class RfidReaderHardware {
public event EventHandler Received;
public RfidReaderHardware() {
syncContext = System.Threading.SynchronizationContext.Current;
}
protected void OnReceived(EventArgs e) {
if (syncContext == null) FireReceived(e);
else syncContext.Send((_) => FireReceived(e), null);
}
protected void FireReceived(EventArgs e) {
var handler = Received;
if (handler != null) Received(this, e);
}
private System.Threading.SynchronizationContext syncContext;
}

Background Task sometimes able to update UI?

I just answered a question about whether a Task can update the UI. As I played with my code, I realized I'm not clear myself on a few things.
If I have a windows form with one control txtHello on it, I'm able to update the UI from a Task, it seems, if I immediately do it on Task.Run:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Task.Run(() =>
{
txtHello.Text = "Hello";
});
}
}
However if I Thread.Sleep for even 5 milliseconds, the expected CrossThread error is thrown:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Task.Run(() =>
{
Thread.Sleep(5);
txtHello.Text = "Hello"; //kaboom
});
}
}
I'm not sure why that happens. Is there some sort of optimization for an extremely short running Task?
You didn't post the exception stack trace, but I expect that it looked something like this:
System.InvalidOperationException: Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.set_WindowText(String value)
at System.Windows.Forms.TextBoxBase.set_WindowText(String value)
at System.Windows.Forms.Control.set_Text(String value)
at System.Windows.Forms.TextBoxBase.set_Text(String value)
at System.Windows.Forms.TextBox.set_Text(String value)
at WindowsFormsApplicationcSharp2015.Form1.<.ctor>b__0_0() in D:\test\WindowsFormsApplicationcSharp2015\Form1.cs:line 27
We can see that the exception is thrown from the Control.Handle getter property. And in fact, if we look at the source code for that property, there it is, as expected:
public IntPtr Handle {
get {
if (checkForIllegalCrossThreadCalls &&
!inCrossThreadSafeCall &&
InvokeRequired) {
throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,
Name));
}
if (!IsHandleCreated)
{
CreateHandle();
}
return HandleInternal;
}
}
The interesting part is when we look at the code that calls Control.Handle. In this case, that's the Control.WindowText setter property:
set {
if (value == null) value = "";
if (!WindowText.Equals(value)) {
if (IsHandleCreated) {
UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value);
}
else {
if (value.Length == 0) {
text = null;
}
else {
text = value;
}
}
}
}
Notice that the Handle property is only invoked if IsHandleCreated is true.
And for completeness, if we look at the code for IsHandleCreated we see the following:
public bool IsHandleCreated {
get { return window.Handle != IntPtr.Zero; }
}
So, the reason you don't get the exception, is because by the time the Task executes, the window handle hasn't been created yet, which is to be expected since the Task starts in the form's constructor, that is, before the form is even displayed.
Before the window handle is created, modifying a property doesn't yet require any work from the UI thread. So during this small time window at the start of your program, it would seem that it is possible to invoke the methods on control instances from a non-UI thread without getting the "cross thread" exception. But clearly, the existence of this special small time window doesn't change the fact that we should always make sure to invoke control methods from the UI thread to be safe.
To prove the point that the timing of the window handle creation is the determining factor in getting (or not) the "cross thread" exception, try modifying your example to force the creation of the window handle before you start the task, and notice how you will now consistently get the expected exception, even without a sleep:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Force creation of window handle
var dummy = txtHello.Handle;
Task.Run(() =>
{
txtHello.Text = "Hello"; // kaboom
});
}
}
Relevant documentation: Control.Handle
If the handle has not yet been created, referencing this property will force the handle to be created.

Call methods in WPF window from main thread

Use case
I'm developing a small application in C# that is called by another application to retrieve data from the Internet. It runs as a process on its own, but almost all of the interaction with it, is managed by the calling application. Therefor it does not have a GUI. However I'd like to add a progress bar using WPF that is shown during certain data retrievals that could take up to a minute. It's fairly easy to make an estimate of how much work is done and how much is left and therefor I find a progress bar suitable.
Research done
I have a fair understanding of threading after reading large parts of Albahari's pdf on threading (http://www.albahari.info/threading/threading.pdf). I have also read through a lot of posts on SO and MSDN in this matter. Most posts suggest the use of a background worker for the time consuming data retrieval while keeping the GUI in the main thread and therefor suggest solutions using a background worker. That feels awkward in this scenario though, where the main task is data retrieval and not GUI interaction.
I've spend a bunch of hours trying to make sense of different tutorials and forum posts while trying to conform them to my problem, but I have not succeeded and now I'm pretty much back to square one. Basically I'd like to end up with the following two classes outlined below:
ProgressBarWindow
public partial class ProgressBarWindow : Window
{
public ProgressBarWindow()
{
InitializeComponent();
}
public void setValue(int value)
{
// This function should be available from the main thread
}
}
Querier
Public class Querier
{
public List<Item> getItems()
{
// call ProgressBarWindow.setValue(0);
...
// call ProgressBarWindow.setValue(100);
// call ProgressBarWindow.Close();
}
}
It's my understanding that UI must run under single threads and therefor my ProgressBarWindow object could not be instantiated in a new thread while at the same time be available to the main thread (kind of).
Dispatcher.BeginInvoke appears to be my savior here but so far I haven't been able to figure out what should go into the Querier class and what to go in the ProgressBarWindow class. How can I make the two threads interact with the same instance of ProgressBarWindow?
Please ask if you need more details and I will try to clarify.
You can use the Progress class to update the UI with the current progress of a long running operation.
First create an instance of Progress in your UI:
Progress<int> progress = new Progress<int>(currentProgress =>
{
progressBar.Value = currentProgress;
//todo do other stuff
});
Then pass it to the long running process:
public List<Item> getItems(IProgress<int> progress)
{
progress.Report(0);
//todo do something
progress.Report(100);
}
Here is a generic function which i generally use:
public static void Invoke(this UIElement element,Action action)
{
element.Dispatcher.Invoke(action, null);
}
And to use it, simply call:
this.Invoke(() => ProgressBarWindow.SetValue(0));
So, in the getItems() function, you would have something along the lines of:
public List<Item> getItems()
{
ProgressBarWindow wnd;
MainWindow.Invoke(() => wnd = new ProgressBarWindow())
MainWindow.Invoke(() => wnd.SetValue(0))
...
MainWindow.Invoke(() => wnd.SetValue(100))
MainWindow.Invoke(() => wnd.Close())
}
Make sure you always have a way to get to the main window is anything (the one running from either App.xml, or App.Run(...). You can then issue any GUI actions through it (even if you have to create a new Loader window for example, as long as it's done within the main thread)
App.xaml
public partial class App : Application
{
private void Application_Startup_1(object sender, StartupEventArgs e)
{
Task.Factory.StartNew<List<int>>(() => Querier.GetItems());
}
}
ProgressBarWindow.xaml.cs
public partial class ProgressWindow : Window
{
public ProgressWindow()
{
InitializeComponent();
Querier.Start +=()=> Visibility = Visibility.Visible;
Querier.Stop += () => Visibility = Visibility.Collapsed;
Querier.ReportProgress +=OnReportProgress;
}
public void OnReportProgress(int value)
{
txtBox.Text = value.ToString();
}
}
ProgressBarWindow.xaml
<Grid>
<TextBox x:Name="txtBox"></TextBox>
</Grid>
Querier
public class Querier
{
public static event Action Start;
public static event Action Stop;
public static event Action<int> ReportProgress;
public static List<int> GetItems()
{
if (Start != null)
App.Current.Dispatcher.BeginInvoke(Start,null);
for (int index = 0; index <= 10; index++)
{
Thread.Sleep(200);
if (ReportProgress != null)
App.Current.Dispatcher.BeginInvoke(ReportProgress, index*10);
}
if (Stop != null)
App.Current.Dispatcher.BeginInvoke(Stop, null);
return Enumerable.Range(1, 100).ToList();
}
}
I am just trying to give an idea hope this will help.

ManualResetEvent WaitOne blocks the owner Thread of my CollectionView

I've written a WPF WizardFramework which performs some actions in the background using some BackgroundWorker. While processing it can happen that I have to update an ObservableCollection which is bound to my UI.
For this case I've written a ThreadableObservableCollection, which provides threadsafe methods for Insert, Remove and RemoveAt. Though I'm using .NET 4.5 I was not able to get BindingOperations.EnableCollectionSynchronization working without many other invalid access exceptions. My Collection looks like:
public class ThreadableObservableCollection<T> : ObservableCollection<T>
{
private readonly Dispatcher _dispatcher;
public ThreadableObservableCollection()
{
_dispatcher = Dispatcher.CurrentDispatcher;
}
public void ThreadsafeInsert(int pos, T item, Action callback)
{
if (_dispatcher.CheckAccess())
{
Insert(pos, item);
callback();
}
else
{
_dispatcher.Invoke(() =>
{
Insert(pos, item);
callback();
});
}
}
[..]
}
This is working as expected, while I am using the wizard in my application. Now I'm using NUnit to write some integrationtests for the application.
There's a listener which waits for the WizardViewModel to finish it's work and looking for some pages which are injected in the Steps-Collection. After the asyncrone work is done I can use Validate to check the viewmodel state.
Unfortunately I'm using a ManualResetEvent to wait for the wizard to close. This looks like following:
public class WizardValidator : IValidator, IDisposable
{
private WizardViewModel _dialog;
private readonly ManualResetEvent _dialogClosed = new ManualResetEvent(false);
[..]
public void ListenTo(WizardViewModel dialog)
{
_dialog = dialog;
dialog.RequestClose += (sender, args) => _dialogClosed.Set();
dialog.StepsDefaultView.CurrentChanged += StepsDefaultViewOnCurrentChanged;
_dialogClosed.WaitOne();
}
[..]
}
Now there's a problem:
While the Application is running the UI Thread is not blocked, the Collection can be updated without any problems. But in my testcases the "main" Thread where I initialize the ViewModel (and because of that the Collections) is an AppDomainThread which is blocked by the testcode. Now my ThreadsafeInsert wants to update the collection but cannot use the AppDomain Thread.
But I have to wait for the wizard to finish, how can I solve this kind of deadlock? Or is there a more elegant solution for this one?
edit:
I worked around this problem with a check if there's a user interface, and only then I invoke on the Application-Thread, otherwise I change the collection intentionally on another thread. This does not prevent the exception, but it is not recognized from the test... the items are inserted nevertheless, only the NotifyCollectionChanged-Handler is not called (which is only used in the UI anyway).
if (Application.Current != null)
{
Application.Current.Dispatcher.Invoke(() =>
{
Steps.Insert(pos, step);
stepsView.MoveCurrentTo(step);
});
}
else
{
new Action(() => Steps.Insert(pos, step)).BeginInvoke(ar => stepsView.MoveCurrentToPosition(pos), null);
}
This is an ugly workaround and I am still interested in a clean solution.
Is there a way to use an alternate Dispatcher to create (e.g.) the whole ViewModel and use this to change my collection?
As I see the main problem that main thread is blocked and other operations are trying to be executed in main thread too? What about not to block main thread, like this:
// helper functions
public void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
public object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
// in your code:
while(!_dialogClosed.WaitOne(200))
DoEvents();
If it will not help then I guess need to try some SynchronisationContext workarounds.
I think the problems boil down to the fact that you create ObservableCollection that is tied to Dispatcher object.
Involving Dispatcher object directly is almost never good idea(as you just witnessed). Instead I would suggest you to see how others have implemented ThreadSafeObservableCollection. This is a little example I put together, it should illustrate the point:
public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
{
private readonly object _lock = new object();
public ThreadSafeObservableCollection()
{
BindingOperations.CollectionRegistering += CollectionRegistering;
}
protected override void InsertItem(int index, T item)
{
lock (_lock)
{
base.InsertItem(index, item);
}
}
private void CollectionRegistering(object sender, CollectionRegisteringEventArgs e)
{
if (e.Collection == this)
BindingOperations.EnableCollectionSynchronization(this, _lock);
}
}

Categories

Resources