2015-03-09 18 views
8

Używam klasy WeakEventManager<TEventSource, TEventArgs> do subskrybowania zdarzeń w języku C#. Subskrypcja zdarzeń działa dobrze, jednak wywoływanie WeakEventManager<TEventSource, TEventArgs>.RemoveHandler z Task nie zawsze usuwa program obsługi - większość (ale nie wszystkie) czasu, w którym program obsługi jest nadal wykonywany po wywołaniu zdarzenia.WeakEventManager RemoveHandler nie zawsze działa, gdy jest asynchronicznie używany

Zostało to zilustrowane w następującym przykładzie.

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

Handler jest usuwany przez cały czas, jednak w większości przypadków zdarzenie jest jeszcze obsługiwane.

Czy popełniam błąd w sposobie wywoływania tych metod? Czy te metody obsługują wywoływanie asynchroniczne? Czy istnieje alternatywne podejście, które zadziałałoby?

Dziękujemy za pomoc z góry.

Odpowiedz

6

To dlatego WeakEventManager „s są przechowywane w bieżącym WeakEventTable które inicjowane w bieżącym wątku (source):

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

I użyć sheduler zadanie basen wątek, który jest domyślnym sheduler. Nazywa czasem AddHandler i RemoveHandler w tym samym wątku. Ale czasami wywołuje RemoveHandler na innym wątku i masz inny WeakEventManager bez żądanego EventSource.

UWAGA: Jeśli typ dziedziczy DispatcherObject następnie przypadki tego typu zależy od tego wątku, w którym zostały utworzone. Jeśli DispatcherObject jest singleton, to jest tworzony jeden na wątek.

Jeśli widzisz DispatcherObject, wywołaj jego metody tylko z wątku, w którym został utworzony. W przeciwnym razie będziesz mieć problemy.

+0

Dzięki, to wyjaśnia, dlaczego widzę to zachowanie. Jak zmienić sposób wywoływania Add/RemoveHandler, aby programy obsługi zostały usunięte w tym samym wątku? – mickeyt

+0

Użyj 'Application.Current.Dispatcher', aby wywołać metody' AddHandler' i 'RemoveHandler'. –

+0

W tym przykładzie nie ma aplikacji "Application.Current". Dzięki za podpowiedź o 'DispatcherObject' choć - bardzo pomocne! – mickeyt

Powiązane problemy