2013-04-12 15 views
11

Se voglio fare un "fire and forget" di qualche codice, ma voglio comunque assicurarmi che la mia memoria sia pulita (per Why does asynchronous delegate method require calling EndInvoke?), il seguente obiettivo raggiungerebbe questo obiettivo?C# È action.BeginInvoke (action.EndInvoke, null) una buona idea?

Action myAction =() => LongRunTime(); 
myAction.BeginInvoke(myAction.EndInvoke,null); 

Mi sono guardato attorno ma non ho visto quel modello utilizzato da nessuna parte. Piuttosto, le persone usano un metodo annonomoyus come loro callback (come The proper way to end a BeginInvoke?) o definiscono un effettivo metodo di callback. Dal momento che non ho visto nessun altro fare questo, mi fa pensare che non funziona o è altrimenti una cattiva idea.

Grazie!

+1

Non vedo alcun problema con l'utilizzo di una conversione del gruppo metodo su un'espressione lambda. – Thorarin

+1

Se è possibile aggiungere una risposta in un modo o nell'altro se questa tecnica non causa una perdita di memoria, quella sarebbe la risposta che sto cercando. Grazie! –

risposta

12

Utilizzando un metodo di conversione di gruppo invece di un delegato va bene, il EndInvoke sarà ancora chiamato a sul vostro Action. Non c'è nient'altro da fare, dato che è un incendio e dimentica la chiamata.

Sfortunatamente, è un po 'difficile dimostrare direttamente che EndInvoke è chiamato, dal momento che Action è un delegato e non possiamo semplicemente aggiungere un punto di interruzione su qualche classe nel BCL.

Questo codice (periodicamente) ispezionare qualche campo privato del IAsyncResult restituito da BeginInvoke, che sembra tenere traccia di se o non EndInvoke è stato ancora chiamato:

public partial class MainWindow : Window 
{ 
    private Timer _timer = new Timer(TimerCallback, null, 100, 100); 
    private static IAsyncResult _asyncResult; 

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    static void LongRunTime() 
    { 
     Thread.Sleep(1000); 
    } 

    void Window_Loaded(object sender, RoutedEventArgs args) 
    { 
     Action myAction =() => LongRunTime(); 
     _asyncResult = myAction.BeginInvoke(myAction.EndInvoke, null); 
    } 

    static void TimerCallback(object obj) 
    { 
     if (_asyncResult != null) 
     { 
      bool called = ((dynamic)_asyncResult).EndInvokeCalled; 
      if (called) 
      { 
       // Will hit this breakpoint after LongRuntime has completed 
       Debugger.Break(); 
       _asyncResult = null; 
      } 
     } 
    } 
} 

ho ricontrollato usando SOS che non ci sono perdite di memoria gestita. Ho anche provato diverse altre prove, ma erano più circostanziali di questo, credo.

Alcuni interessanti che ho scoperto durante la mia indagine: la chiamata myAction.BeginInvoke verrà visualizzata sui profiler utilizzando la strumentazione, ma non lo è lo myAction.EndInvoke.

+0

Non c'è alcun senso a questo che io possa vedere. Basta chiamare LongRunTime() direttamente all'interno dell'attività. –

+3

L'attività era oltre il punto, ma ecco un esempio senza un'attività se preferisci. – Thorarin

+0

+1 Grazie Thorarin! Grandi dettagli e ricerca! –

Problemi correlati