2009-11-11 9 views
10

Warum muss der Delegat die EndInvoke aufrufen, bevor die Methode ausgelöst wird? Wenn ich die EndInvoke (die den Thread blockiert) aufrufen muss, dann ist es nicht wirklich ein asynchroner Aufruf, oder?Warum erfordert die asynchrone Delegatenmethode den Aufruf von EndInvoke?

Hier ist der Code im Versuch zu laufen.

class Program 
    { 
     private delegate void GenerateXmlDelegate(); 

     static void Main(string[] args) 
     { 
      GenerateXmlDelegate worker = new GenerateXmlDelegate(GenerateMainXml); 
      IAsyncResult result = worker.BeginInvoke(null, null); 
     } 

     private static void GenerateMainXml() 
     { 
      Thread.Sleep(10000); 
      Console.WriteLine("GenerateMainXml Called by delegate"); 
     } 
    } 

Antwort

15

Der Grund, warum Sie Speicherlecks zu vermeiden EndInvoke ist anrufen müssen; .Net speichert Informationen über das Ergebnis (oder die Ausnahme) der Funktion, bis Sie EndInvoke aufrufen.

Sie können EndInvoke im Completion-Handler, den Sie BeginInvoke geben, aufrufen und die asynchrone Eigenschaft beibehalten.

EDIT:

Zum Beispiel:

class Program { 
    private delegate void GenerateXmlDelegate(); 

    static void Main(string[] args) { 
     GenerateXmlDelegate worker = new GenerateXmlDelegate(GenerateMainXml); 
     IAsyncResult result = worker.BeginInvoke(delegate { 
      try { 
       worker.EndInvoke(); 
      } catch(...) { ... } 
     }, null); 
    } 

    private static void GenerateMainXml() { 
     Thread.Sleep(10000); 
     Console.WriteLine("GenerateMainXml Called by delegate"); 
    } 
} 

Wenn Sie einen Asynchron-Anruf schießen wollen und vergessen Sie es, können Sie die ThreadPool wie folgt verwenden:

ThreadPool.QueueUserWorkItem(delegate { GenerateMainXml(); }); 
+0

Entschuldigung könnten Sie mein Beispiel erweitern, um zu demonstrieren, was Sie mit Completion Handler meinen? Aus all den Artikeln, die ich gelesen habe, geht hervor, dass durch einfachen Aufruf von BeginInvoke der Methodenaufruf ausgelöst wird. –

+1

Dies ist nur etwas korrekt - es werden keine Speicherlecks auftreten. https://gist.github.com/jcdickinson/9109599. In bestimmten Szenarios wird jedoch ein Tracking mit "Begin/End-Invoke" -Paaren durchgeführt - ein Beispiel ist: Wenn Sie 'End *' nicht auf 'Socket'-Operationen aufrufen, werden Ihre Socket-Leistungsindikatoren völlig außer Kontrolle geraten (kein Speicherleck, die Werte werden nur wild falsch sein). –

6

Wie SLaks sagte, EndInvoke versichert gegen Speicherlecks.

BeginInvoke ist immer noch asynchron; Sehen Sie den folgenden Code:

static void Main() { 
    Func<double> slowCalculator = new Func<double>(PerformSlowCalculation); 
    IAsyncResult slowCalculation = slowCalculator.BeginInvoke(null, null); 

    // lots of stuff to do while slowCalculator is doing its thing 

    Console.WriteLine("Result is {0}", slowCalculator.EndInvoke(slowCalculation)); 
} 

static double PerformSlowCalculation() { 
    double result; 

    // lots and lots of code 

    return result; 
} 

Wenn dieser Code ohne die BeginInvoke/EndInvoke Anrufe geschrieben wurden, würde PerformSlowCalculation haben zu beenden, bevor Main den Rest seiner „viele Sachen“ tun könnte; Auf diese Weise können die beiden zur gleichen Zeit stattfinden.

In Ihrem Beispiel mit GenerateXmlDelegate benötigen Sie immer noch EndInvoke, obwohl Sie nichts zurückgeben. Die Art und Weise dies zu tun ist:

Verwandte Themen