2010-02-12 14 views
11

Wir haben dieses häufige Szenario, in dem wir eine Methode haben, die asynchrone Aktionen ausführt und ein Ereignis auslöst, wenn es fertig ist.Gibt es eine allgemeine Möglichkeit, eine asynchrone Methode zu synchronisieren?

Es gibt Zeiten, in denen wir es synchron stattdessen getan wollen, so dass wir Code haben, der in etwa so aussieht:

ManualResetEvent reset = new ManualResetEvent(false); 
someobject.AsyncActionDone += (sender, args) => reset.Set(); 
someobject.PerformAsyncAction(); 
reset.WaitOne(); 

Gibt es eine Möglichkeit, eine Hilfsmethode zu schreiben, dies zu tun? Ich kann die Aktion übergeben, aber ich bin mir nicht sicher, wie ich etwas übergeben kann, das die Hilfsmethode weiß, welches Ereignis zu hören ist, da es nicht so aussieht, als könnte man einen EventHandler als Parameter übergeben.

Vorzugsweise wird eine Lösung, die nicht Reflexion

es einige Verwirrung scheint nicht erforderlich zu sein, ist dies eine Probe der Klasse Der schreibt Someobject der ist wie:

public class SomeClass 
{ 
    private ExternalServer someServerOverTheNetwork = new ExternalServer(); 

    public event EventHandler AsyncActionDone; 
    public Data SomeData { get; set; } 
    public void PerformAsyncAction() 
    { 
     someServerOverTheNetwork.GetSomeData(OnDataRetrived); 
    } 

    public Data OnDataRetrived(Data someData) 
    { 
     AsyncActionDone(this, new DataEventArgs(someData)); 
    } 
} 
+0

Sind Sie der Implementierer von SomeObject oder ist es ein Bibliothekstyp, der nicht geändert werden kann? –

+0

Wir sind, aber es gibt viele solche Objekte. Die meisten Aktionen umfassen das Senden einer Anfrage an einen Server und das Warten darauf, dass das Ergebnis zurückkommt. Daher sind sie standardmäßig asynchron. – Davy8

+0

Wir haben eine Methode, um dies für eine unserer gängigsten Aktionen zu tun, indem wir eine Liste von Objekten vom Server zwischenspeichern, dies hängt jedoch von der Kenntnis des genauen Objekttyps und des zu hörenden Ereignisses ab. Ich möchte in der Lage sein, in welchem ​​Ereignis zu hören, damit wir doppelten Code reduzieren können, da die einzigen Dinge, die sich unterscheiden, die Methode und das Ereignis sind, das nach Abschluss ausgelöst wird. – Davy8

Antwort

4

Ich würde die Implementierung der Asynchronous Design Pattern in den Objekten, die asynchrone Operation durchführt, betrachten.

public object Operation(object arg) 
{ 
    var ar = BeginOperation(arg, null, null); 

    return EndOperation(ar); 
} 

public IAsyncResult BeginOperation(object arg, AsyncCallback asyncCallback, object state) 
{ 
    AsyncResult asyncResult = new AsyncResult(asyncCallback, state); 

    // Lauch the asynchronous operation 

    return asyncResult; 
} 

private void LaunchOperation(AsyncResult asyncResult) 
{ 
    // Do something asynchronously and call OnOperationFinished when finished 
} 

private void OnOperationFinished(AsyncResult asyncResult, object result) 
{ 
    asyncResult.Complete(result); 
} 


public object EndOperation(IAsyncResult asyncResult) 
{ 
    AsyncResult ar = (AsyncResult)asyncResult; 

    return ar.EndInvoke(); 
} 

Mit diesem Muster haben Sie die Flexibilität auf dem Objekt mehr gleichzeitigen asynchronen Betrieb mit.

Hinweis: Sie können problemlos eine Implementierung einer generischen AsyncResult-Klasse im Internet finden.

Edit:

Wie Sie das aktuelle Design behalten möchten, wenn Sie alle Ihre Objekt nur eine asynchrone Operation haben, dann könnten Sie eine IAsyncOperation Schnittstelle definieren und implementieren sie in allen Objekt.

public interface IAsyncOperation 
{ 
    event EventHandler AsyncActionDone; 
    void PerformAsyncAction(); 
} 

Dann könnten Sie haben:

public static CallSynchronously(IAsyncOperation asyncOperation) 
{ 
    ManualResetEvent reset = new ManualResetEvent(false); 
    asyncOperation.AsyncActionDone += (sender, args) => reset.Set(); 
    asyncOperation.PerformAsyncAction(); 
    reset.WaitOne(); 
} 

Wenn Ihre Objekte mehrere asynchrone Operation, dann ohne Reflexion enthalten kann ich glaube, es gibt keine Möglichkeit, zu erreichen, ist das, was Sie tun wollen, aber man konnte noch definieren eine synchrone Version des gesamten asynchronen Vorgangs, der das ManualResetEvent umschließt.

public void PerformAction() 
{ 
    ManualResetEvent reset = new ManualResetEvent(false); 
    this.AsyncActionDone += (sender, args) => reset.Set(); 
    this.PerformAsyncAction(); 
    reset.WaitOne(); 
} 
+0

Ich bin auf der Suche nach einer Lösung, bei der die Klasse von 'someobject' nicht verändert wird. Das aktuelle Muster existiert bereits in Dutzenden von Klassen in unserem System. – Davy8

+0

Bearbeitete Frage mit hoffentlich besserer Erklärung – Davy8

+0

Bearbeitete die Antwort auch –

3

Wenn ich Ihre Drift zu fangen, können Sie einfach Delegierten in der folgenden Weise zu verwenden, einen Delegaten erstellen für Sie Aufruf funktionieren, läßt es

public delegate string AsyncDelegate(); 
ruft dann ein Proxy-Funken erstellen Ion wie folgt:

public static void ExecuteSync(AsyncDelegate func) 
    { 
     ManualResetEvent reset = new ManualResetEvent(false); 
     int threadId; 
     func.BeginInvoke((a)=>reset.Set(), null); 
     reset.WaitOne(); 
    } 

Das ist alles können Sie es komplizierter etwas machen eine andere Funktion delegieren durch Zugabe nach Beendigung oder so ähnlich zu laufen ..

Viel Spaß!

+0

Wenn ich mich nicht täusche, nimmt man an, dass du schon eine synchrone Funktion hast, oder? Wenn ich 'someobject.PerformAsyncAction();' aufruft, reiht es die Aktion auf und kehrt sofort zurück. Ich sehe nicht, ob ich meine Funktion an Ihre Methode weitergegeben habe, sie würde wissen, wann die Aktion tatsächlich ausgeführt wird. – Davy8

+0

Wenn die Aktion abgeschlossen ist, ruft sie den Callback auf, der das ManualResetEvent 'setzt'. Die Hauptfunktion kehrt erst zurück, wenn das ManualResetEvent 'gesetzt' ist. – Foole

+0

Woher weiß es, wann die Aktion abgeschlossen ist? Alle Action-Funktion sendet eine Nachricht und kehrt dann sofort zurück. – Davy8

Verwandte Themen