2015-03-09 13 views
8

Sto utilizzando la classe WeakEventManager<TEventSource, TEventArgs> per iscriversi agli eventi in C#. L'abbonamento all'evento funziona bene, tuttavia chiamare WeakEventManager<TEventSource, TEventArgs>.RemoveHandler da un Task non rimuove sempre il gestore - la maggior parte (ma non tutto) del tempo in cui il gestore viene ancora eseguito quando l'evento viene attivato.WeakEventManager RemoveHandler non funziona sempre se chiamato in modo asincrono

Ciò è illustrato nel seguente esempio.

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

Il gestore viene rimosso tutto il tempo, tuttavia nella maggior parte dei casi l'evento viene comunque gestito.

Sto facendo un errore nel modo in cui vengono chiamati questi metodi? Questi metodi supportano l'essere chiamati in modo asincrono? Esiste un approccio alternativo che potrebbe funzionare?

Molte grazie per il vostro aiuto in anticipo.

risposta

6

È perché WeakEventManager 's sono memorizzati in una corrente, che WeakEventTable inizializzato per il thread corrente (source):

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

e si utilizza il pool di thread compito sheduler che è lo sheduler predefinita. Chiama AddHandler e RemoveHandler sullo stesso thread a volte. Ma a volte invoca RemoveHandler su un thread diverso e si dispone di un altro WeakEventManager senza lo EventSource richiesto.

NOTA: Se un tipo eredita da DispatcherObject, le istanze di questo tipo dipendono dal thread in cui sono state create. Se uno DispatcherObject è singleton, ne viene creato uno per thread.

Quindi se si vede un DispatcherObject quindi chiamare i suoi metodi solo dal thread in cui è stato creato. Altrimenti, avrai problemi.

+0

Grazie, questo spiega perché sto vedendo questo comportamento. Come cambierei il modo in cui sto chiamando Aggiungi/RimuoviHandler in modo che i gestori vengano rimossi sullo stesso thread? – mickeyt

+0

Utilizzare 'Application.Current.Dispatcher' per invocare i metodi' AddHandler' e 'RemoveHandler'. –

+0

In questo esempio non c'è 'Application.Current'. Grazie per il suggerimento su 'DispatcherObject' - molto utile! – mickeyt

Problemi correlati