I am using the WeakEventManager<TEventSource, TEventArgs>
class in order to subscribe to events in C#. Event subscription works fine, however calling WeakEventManager<TEventSource, TEventArgs>.RemoveHandler
from a Task
does not always remove the handler - most (but not all) of the time the handler is still executed when the event fires.
This is illustrated in the following example.
public class EventSource
{
public event EventHandler Fired = delegate { };
public void FireEvent()
{
Fired(this, EventArgs.Empty);
}
}
class Program
{
private static bool added, removed, handled;
static void Main(string[] args)
{
for (int i = 1; i <= 100; i++)
{
added = removed = handled = false;
var source = new EventSource();
AddHandlerAsync(source).Wait();
RemoveHandlerAsync(source).Wait();
source.FireEvent();
if (removed && handled) Console.WriteLine("Event handled after removal!");
else Console.WriteLine("----------------------------");
}
Console.ReadKey();
}
private async static Task AddHandlerAsync(EventSource source)
{
await Task.Run(() =>
{
System.Windows.WeakEventManager<EventSource, EventArgs>.AddHandler(source, "Fired", HandleEvent);
added = true;
});
}
private async static Task RemoveHandlerAsync(EventSource source)
{
await Task.Run(() =>
{
System.Windows.WeakEventManager<EventSource, EventArgs>.RemoveHandler(source, "Fired", HandleEvent);
removed = true;
});
}
private static void HandleEvent(object sender, EventArgs e)
{
handled = true;
}
}
The handler is removed all of the time, however in most cases the event is still handled.
Am I making an error in the way that these methods are called? Do these methods support being called asynchronously? Is there an alternative approach that would work?
Many thanks for your help in advance.
It's because WeakEventManager
's are stored in a current WeakEventTable
which initialized for the current thread (source):
[ThreadStatic]
private static WeakEventTable _currentTable; // one table per thread
And you use the thread pool task sheduler which is the default sheduler. It calls AddHandler
and RemoveHandler
on the same thread sometimes. But sometimes it invokes RemoveHandler
on a different thread and you have another WeakEventManager
without the requested EventSource
.
NOTE: If a type inherits from DispatcherObject
then instances of this type depends on the thread in which they are created. If a DispatcherObject
is singleton then it is created one per thread.
So if you see a DispatcherObject
then call its methods only from thread in which it was created. Otherwise, you will have problems.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With