Freigeben über


Übersicht über asynchrone Entwurfsmuster

Asynchrone Muster bieten u. a. die Neuerung, dass der Aufrufer entscheidet, ob ein bestimmter Aufruf asynchron ist oder nicht. Ein aufgerufenes Objekt muss keine zusätzlichen Programmierschritte ausführen, damit das asynchrone Verhalten vom Client unterstützt wird; die Voraussetzungen dafür sind durch die asynchronen Delegaten im Muster gegeben. Der Unterschied zwischen Aufrufer und aufgerufenen Objektansichten wird durch die Common Language Runtime behandelt. Das aufgerufene Objekt kann das asynchrone Verhalten explizit unterstützen – entweder, weil es leichter zu implementieren ist als eine allgemeine Architektur, oder weil nur das asynchrone Verhalten der Aufrufer unterstützt werden soll. Grundsätzlich wird jedoch empfohlen, dass solche aufgerufenen Objekte zur Anzeige asynchroner Operationen dem asynchronen Entwurfsmuster folgen.

Eine weitere Innovation des asynchronen Musters ist die Typsicherheit. Vor allem für asynchrone Delegaten kann ein Sprachcompiler, der .NET Framework und Common Language Runtime zum Ziel hat, sichere Methodensignaturen für Start- und Endvorgänge eingeben, die auf die reguläre Invoke-Methode, z. B. für BeginInvoke und EndInvoke verweisen. Das ist von Bedeutung, da der Compiler den synchronen Aufruf während der Start- und Endvorgänge für den asynchronen Delegaten abbricht und es ermöglicht, nur gültige Parameter zu übergeben.

Hinter diesem Muster stehen folgende grundlegende Gedanken:

  1. Der Aufrufer entscheidet, ob ein bestimmter Aufruf asynchron ist.
  2. Das aufgerufene Objekt muss zusätzliche Programmierschritte ausführen, um asynchrones Verhalten durch die Clients zu unterstützen; das ist jedoch nicht erforderlich. Der Unterschied zwischen Aufrufer und aufgerufenen Objektansichten muss durch die Common Language Runtime-Infrastruktur zu handhaben sein.
  3. Das aufgerufene Objekt kann das asynchrone Verhalten explizit unterstützen – entweder, weil es leichter zu implementieren ist als eine allgemeine Architektur, oder weil nur das asynchrone Verhalten der Aufrufer unterstützt werden soll. Grundsätzlich wird jedoch empfohlen, dass solche aufgerufenen Objekte zur Anzeige asynchroner Operationen dem asynchronen Entwurfsmuster folgen.
  4. Der Compiler erzeugt für asynchrone Delegaten typsichere Methodensignaturen für BeginInvoke und EndInvoke.
  5. .NET Framework stellt Dienste zur Verfügung, die zur Unterstützung eines asynchronen Programmiermodells erforderlich sind. Es folgt eine Liste mit Beispielen für solche Dienste:
    • Synchronisierungsprimitive, z. B. Überwachungen und Lese-/Schreibsperren.
    • Threads und Threadpools.
    • Synchronisierungskonstrukte, z. B. Container, die das Warten auf Objekte unterstützen.
    • Anzeige der zugrunde liegenden Infrastrukturabschnitte, z. B. IMessage-Objekte und Threadpools.

Das Muster zerlegt einen synchronen Aufruf in seine Bestandteile: eine Startoperation, eine Endoperation und ein Result-Objekt. Betrachten Sie das folgende Beispiel. Hier wird der Factorize-Methode eine beträchtliche Zeitspanne bis zum Abschluss eingeräumt.

public class PrimeFactorizer
{
   public bool Factorize(int factorizableNum, ref int primefactor1, ref int primefactor2)
   {
      // Determine whether factorizableNum is prime.
      // If it is prime, return true. Otherwise, return false.
      // If it is prime, place factors in primefactor1 and primefactor2.
   }
}

Wenn das asynchrone Muster zum Einsatz kommt, fügt der Autor der Klassenbibliothek die Methoden BeginFactorize und EndFactorize ein, die die synchrone Operation in zwei asynchrone Operationen untergliedern:

public class PrimeFactorizer
{
   public bool Factorize(
int factorizableNum, 
ref int primefactor1, 
ref int primefactor2)
   {
      // Determine whether factorizableNum is prime.
      // if it is prime, return true; otherwise return false.
      // if it is prime, place factors in primefactor1 and primefactor2
   }

   public IAsyncResult BeginFactorize(
int factorizableNum, 
ref int primefactor1, 
ref int primefactor2, 
AsyncCallback callback, 
Object state)
   {
     // Begin factoring asynchronously, and return a result object,
   }

   public bool EndFactorize(
ref int primefactor1, 
ref int primefactor2,
IAsyncResult asyncResult
)
   {
     // End (or complete) the factorizing, 
     // return the results, 
     // and obtain the prime factors.
   }
}

Der Server zerlegt die asynchrone Operation in ihre beiden logischen Bestandteile: einen Teil, der die Eingabe vom Client aufnimmt und die asynchrone Operation aufruft, und einen Teil, der dem Client die Ergebnisse der asynchronen Operation zur Verfügung stellt.

Neben der Eingabe, die für die asynchrone Operation erforderlich ist, nimmt der erste Teil auch einen AsyncCallback-Delegaten auf, der bei Abschluss der asynchronen Operation aufgerufen wird. Der erste Teil gibt das Objekt in Warteposition zurück, das die IAsyncResult-Schnittstelle implementiert, die vom Client verwendet wird, um den Status der asynchronen Operation zu bestimmen.

Der Server verwendet das an den Client zurückgegebene Wait-fähige Objekt außerdem zur Aufrechterhaltung des mit der asynchronen Operation verknüpften Zustands. Der Client verwendet den zweiten Teil, um die Ergebnisse der asynchronen Operation durch Bereitstellung des Wait-fähigen Objekts abzurufen.

Optionen, die dem Client zum Starten asynchroner Operationen zur Verfügung stehen:

  1. Bereitstellung des Rückrufdelegaten beim Starten des asynchronen Aufrufs.

    public class Driver1
    {
      public PrimeFactorizer primeFactorizer;
    
      public void Results(IAsyncResult asyncResult)
      {
        int primefactor1=0; 
        int primefactor2=0;
    
        bool prime = primeFactorizer.EndFactorize(
    ref primefactor1, 
    ref primefactor2,
    asyncResult);
      }
    
      public void Work()
      {
        int factorizableNum=1000589023, 
        int primefactor1=0; 
        int primefactor2=0;
        Object state = new Object();
    
        primeFactorizer = new PrimeFactorizer();
        AsyncCallback callback = new Callback(this.Results);
        IAsyncResult asyncResult = primeFactorizer.BeginFactorize(
    factorizableNum, 
    ref primefactor1, 
    ref primefactor2, 
    callback, 
    state);
      }
    }
    
  2. Keine Bereitstellung des Rückrufdelegaten beim Starten der asynchronen Operation.

    public class Driver2
    {
      public static void Work()
      {
        int factorizableNum=1000589023, 
        int primefactor1=0; 
        int primefactor2=0;
        Object state = new Object();
    
        PrimeFactorizer primeFactorizer = new PrimeFactorizer();
        AsyncCallback callback = new Callback(this.Results);
        IAsyncResult asyncResult = primeFactorizer.BeginFactorize(
    factorizableNum, 
    ref primefactor1, 
    ref primefactor2, 
    callback, 
    state);
    
        bool prime = primeFactorizer.EndFactorize(
    ref primefactor1, 
    ref primefactor2,
    asyncResult);
      }
    }
    

Siehe auch

Asynchrone Methodensignaturen | IAsyncResult-Schnittstelle | AsyncCallback-Delegat für asynchrone Operationen | Einschließen asynchroner Aufrufe