Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WeakEventManager RemoveHandler does not always work when called asynchronously

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.

like image 338
mickeyt Avatar asked Mar 09 '15 15:03

mickeyt


1 Answers

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.

like image 123
Yoh Deadfall Avatar answered Oct 13 '22 00:10

Yoh Deadfall