WeakEventManager RemoveHandler在异步调用时并不总是有效

时间:2015-03-09 15:16:30

标签: c# .net asynchronous

我正在使用WeakEventManager<TEventSource, TEventArgs>类来订阅C#中的事件。事件订阅工作正常,但是从Task调用WeakEventManager<TEventSource, TEventArgs>.RemoveHandler并不总是删除处理程序 - 当事件触发时处理程序仍然执行的大部分时间(但不是全部)。

以下示例说明了这一点。

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

处理程序一直被删除,但在大多数情况下仍会处理事件。

我是否在调用这些方法时出错?这些方法是否支持异步调用?是否有其他方法可行?

非常感谢您的帮助。

1 个答案:

答案 0 :(得分:8)

这是因为WeakEventManager存储在为当前线程(source)初始化的当前WeakEventTable中:

[ThreadStatic]
private static WeakEventTable   _currentTable;  // one table per thread

你使用线程池任务sheduler,它是默认的sheduler。它有时会在同一个线程上调用AddHandlerRemoveHandler。但有时它会在不同的主题上调用RemoveHandler,而您有另一个WeakEventManager没有请求的EventSource

注意:如果类型继承自DispatcherObject,则此类型的实例取决于创建它们的线程。如果DispatcherObject是单身,则每个线程创建一个。

因此,如果您看到DispatcherObject,则仅从创建它的线程调用其方法。否则,你会遇到问题。