Laden...

async/await: Verständnisfrage zur Ausführung in synchronen Methoden

Erstellt von Ares vor 10 Jahren Letzter Beitrag vor 10 Jahren 11.264 Views
A
Ares Themenstarter:in
167 Beiträge seit 2005
vor 10 Jahren
async/await: Verständnisfrage zur Ausführung in synchronen Methoden

Hallo,

Ich verwende sqlite-winrt um innerhalb eines Windows Phone 8 Projektes auf eine SQLite Datenbank zuzugreifen.

Alle wesentlichen Methoden in sqlite-winrt sind asynchron, z.B. .OpenAsync(), etc.

Die Datenbank in meiner App wird wirklich klein sein. Es macht daher nicht wirklich Sinn asynchron zu arbeiten weil sich alle Operationen ziemlich schnell bearbeiten lassen.

Wie verwende ich nun in meiner "synchronen App" die asynchronen Methoden von sqlite-winrt?


private async void ConntectToDatabase() {
    database = new Database(ApplicationData.Current.LocalFolder, databaseName);
    await database.OpenAsync();
}

await ist hier quasi genau das Schlüsselwort, dass ich suche. Die asynchrone Methode wird aufgerufen und auf deren Erledigung gewartet. Insgesamt arbeitet ConnectToDatabase also synchron.

Das geht aber nur, wenn die Methode durch das Hinzufügen von "async" selber als asynchron ausgezeichnet wird. Aber ist das wirklich das korrekt vorgehen?

Die Methode soll ja eben nicht asynchron sein, sondern synchron ausgeführt werden. Verstehe ich die Bedeutung des Schlüsselwortes async falsch oder ist hier die Verwendung von async und await generell falsch?

async hat ja auch noch weitere Einschränkungen. z.B. kann man die Methode nicht einfach von void auf bool ändern, weil async Methoden nur void, Task oder Task<T> liefern dürfen.

Wie verwendet mal also asynchrone Aufrufe richtig in synchronen Methoden?

6.911 Beiträge seit 2009
vor 10 Jahren

Hallo Ares,

Es macht daher nicht wirklich Sinn asynchron zu arbeiten weil sich alle Operationen ziemlich schnell bearbeiten lassen.

Ziemlich schnell ist auch relativ. Alles was länger dauert als ca. 1/10s sollte asynchron zum GUI ausgeführt werden. Siehe auch [FAQ] Warum blockiert mein GUI? - gilt nach wie vor.

Verstehe ich die Bedeutung des Schlüsselwortes async falsch

Ja.

Die asynchrone Methode wird aufgerufen und auf deren Erledigung gewartet.

Es wird nicht gewartet, sondern bei await wird eine Continuation für den Rest der Methode angehängt, die dann ausgeführt wird, sobald der asynchrone Vorgang fertig ist. Hier also nur der Rücksprung zum Caller. Es ist also insgesam asynchron.

async in der Methodensignatur ist eigentlich nur nötig um dem Compiler mitzuteilen dass ein vorhandes await keine Variable sondern ein Schlüsselwort ist - das war/ist nötig damit auch ältere Codes, in denen zufällig await verwendet wurde, sich auch noch korrekt kompilieren lassen.

oder ist hier die Verwendung von async und await generell falsch?

Die Verwendung ist schon (fast) korrekt. Anstatt void sollte aber besser Task zurückgegeben werden. Dann kannst du die Methode aber gleich so umändern:


private Task ConntectToDatabaseAsync() {
    database = new Database(ApplicationData.Current.LocalFolder, databaseName);
    return database.OpenAsync();
}

Das "Problem" von async/await ist, dass es "viral" ist. D.h. sobald du irgendwo asynchrone Vorgänge einführst, sollten sich diese durch die gesamte Anwendung ziehen. Irgendwo auf eine Task zu warten (z.B. per Wait od. per Result) macht eben keinen Sinn, da an dieser Stelle die GUI blockieren kann/wird.

kann man die Methode nicht einfach von void auf bool ändern,

Aber auf Task<bool>.

Wie verwendet mal also asynchrone Aufrufe richtig in synchronen Methoden?

Am besten gar nicht. Entweder synchron od. asynchron.
Sollte es dennoch sein müssen, so musst du halt auf den Task warten, aber nicht mit await, sondern mit der Wait-Methode od. bei Task<T> auch durch Zugriff auf die Result-Eigenschaft.

Siehe auch Async/Await FAQ

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

A
Ares Themenstarter:in
167 Beiträge seit 2005
vor 10 Jahren

Super, vielen Dank für die ausführliche und informative Antwort! Ich werde mich damit noch einmal weiter in die Lektüre stürzen. Die verlinkte Seite ist wirklich gut. Zuvor habe ich mich an einem Blog-Artikel orientiert, der mich scheinbar mehr verwirrt als beraten hat 😃

Hinweis von Abt vor 10 Jahren

Threads zusammengefügt

A
Ares Themenstarter:in
167 Beiträge seit 2005
vor 10 Jahren
TAP, async, await & Co - Verständnisfrage

Hallo,

in einem Windows Phone 8 Projekt verwende ich sqlite-winrt um auf eine SQLite Datenbank zuzugreifen. Alle wesentlichen Methoden in sqlite-winrt sind asynchron, z.B. .OpenAsync(), etc.

Nach einem ersten Thread async/await: Verständnisfrage zur Ausführung in synchronen Methoden habe ich noch einmal zusätzlich recherchiert.

Die generell Bedeutung von async und await habe ich (denke ich) verstanden, ich verstehe aber die generelle Funktion des Task-based Asynchronous Pattern (TAP) allgemein nicht.

In der Beschreibung von Microsoft (Download "Task-based Asynchronous Pattern" from Official Microsoft Download Center, DOCX-Format) wird TAP mit anderen asynchronen Pattern vergleichen.

Bei APM und EAP ist es so, dass eine synchrone Methode einen asynchronen Aufruf startet und dann irgendwann nach dessen Ende Eine Rückmeldung über einen Callback oder ein Event erhält. Man weiß also wann der asynchronen Aufruf zu Ende ist und kann entsprechend weiterarbeiten.

Genau das fehlt mir beim TAP. Wie funktioniert das hier?

Bei sqlite-winrt ist z.B. die Methode zum Herstellen der Verbindung zur Datenbank asynchron. Wäre die Methode synchron würde das Ganze z.B. so aussehen:


public void SyncMethode() {
  ...
  Connect();
  WorkWithDB();
  ...
}

public void Connect() {
   database.Open();
   InitDB(database);
   MachNochWasMitDB();
}

Da Open() synchron ausgeführt wird gibt es kein Problem. InitDB(), MachNochWasMitDB() und später WorkWithDB() wird erst ausgeführt wenn die Datenbank wirklich geöffnet werden konnte.

ABER: Open ist nicht synchron sondern steht nur als asynchrones OpenAsync() zur Verfügung:


public void SyncMethode() {
  ...
  Connect();
  WorkWithDB();
  ...
}


// Lösung1: Einfach OpenAsync verwenden
public void Connect() {
   database.OpenAsync();
   InitDB(database);
   MachNochWasMitDB();
}

// Lösung2: Connect async machen
public async Task ConnectAsync() {
   await database.OpenAsync();
   InitDB(database);
   MachNochWasMitDB();
}

Lösung1 funktioniert natürlich nicht. Nach dem Aufruf von OpenAsync() wird direkt weitergearbeitet obwohl die Verbindung (vielleicht) noch gar nicht hergestellt wurde. InitDB, etc. laufen also vermutlich ins Leere.

Lösung2 ist schon besser. Durch das await werden InitDB und MachNachWasMitDB() erst ausgeführt wenn OpenAsync() komplett ausgeführt wurde (richtig?). Gleichzeitig geht die Kontrolle nach dem Aufruf von await database.OpenAsync() aber direkt zurück an die synchrone Methode SyncMethode(). Dort wird WorkWithDB() aufgerufen obwohl diese noch gar nicht bereit ist...

Jetzt könnte man natürlich auch in SyncMethode() ein await verwenden, aber dann bekommt man das gleiche Problem in der Methode die SyncMethode() aufruft. Die Kette würde sich bis zum allerersten Aufruf im Programm fortsetzten.

Wie löst man das?

Wie erfährt SyncMethode() (oder eine andere Synchrone Methode) davon, dass ConnectAsync() vollständig ausgeführt wurde und man mit WorkWithDB() weiter machen kann?

Bei APM und EAP gäbe es wie gesagt einen Callback oder ein Event, dass entsprechend Bescheid sagen würde. Aber wie läuft das hier?

16.842 Beiträge seit 2008
vor 10 Jahren

Naja, ich glaub Du hast es nicht verstanden 😉
Dein Code bringt quasi nichts, keinerlei Vorteile. Erst wenn Du um das async/await etwas drumrum baust (zB während dem Verbindungsaufbau irgendwas anderes machen kannst) macht es sinn.
Dass Du die DB erst verwenden kannst, wenn eine Verbindung verstehst, ist klar.

Einfaches Beispiel, wieso async/await viel Code spart.
Alles aus Using Asynchronous Methods in ASP.NET MVC 4 (bin halt Webentwickler).

Gegeben ist Code1:

public ActionResult PWG()
{
    ViewBag.SyncType = "Synchronous";
    var widgetService = new WidgetService();
    var prodService = new ProductService();
    var gizmoService = new GizmoService();

    var pwgVM = new ProdGizWidgetVM(
        widgetService.GetWidgets(),
        prodService.GetProducts(),
        gizmoService.GetGizmos()
       );

    return View("PWG", pwgVM);
}

Hier werden alle Get-Anfragen nach und nach aufgerufen. Also NICHT parallel.

Gegeben ist Code 2:

public async Task<ActionResult> PWGasync()
{
    ViewBag.SyncType = "Asynchronous";
    var widgetService = new WidgetService();
    var prodService = new ProductService();
    var gizmoService = new GizmoService();

    var widgetTask = widgetService.GetWidgetsAsync();
    var prodTask = prodService.GetProductsAsync();
    var gizmoTask = gizmoService.GetGizmosAsync();

    await Task.WhenAll(widgetTask, prodTask, gizmoTask);

    var pwgVM = new ProdGizWidgetVM(
       widgetTask.Result,
       prodTask.Result,
       gizmoTask.Result
       );

    return View("PWG", pwgVM);
}

Hier werden alle Get-Methode **zeitgleich **aufgerufen -> einfache Paralellsierung.
==> Minimaler Codeaufwand, einfache Steigerung der Performance.

Damit async/await funktioniert muss sich das eben wie ein roter Faden durch den Code ziehen.

Warum Dein Code schrecklich ist:
Wenn Du ne Methode hast, die suggeriert, dass sie nur eine Connection durchführt, dann sollte auch dessen Inhalt so sein.

Viel eher sollte Dein Code also so aussehen:


public Task ConnectAsync() {
    return database.OpenAsync();
 } 

Ansonsten kannst Du Dich ja an dem MSDN Artikel orientieren, wie in etwa Code aussehen muss, damit async/await adaptiv genutzt werden kann: das ist auch das "Geheimnis" daran. Wenn Du TPL und Co verstanden hast, dann ist async/await auch zum Greifen nahe.
Es kommt aber sehr sehr auf die Struktur des Codes an.

PS: da es keinen Grund gibt zum gleichen Thema einfach nen neuen Thread zu öffnen habe ich beide verschmolzen. Bitte handle in Zukunft auch dementsprechend.

A
Ares Themenstarter:in
167 Beiträge seit 2005
vor 10 Jahren

Naja, ich glaub Du hast es nicht verstanden 😉

Da sind wir uns einig 😃

Dein Code bringt quasi nichts, keinerlei Vorteile. Erst wenn Du um das async/await etwas drumrum baust (zB während dem Verbindungsaufbau irgendwas anderes machen kannst) macht es sinn.

Naja, es sollte klar sein dass es nur ein Beispiel ist. Es geht ja nicht nur um den Verbindungsaufbau sondern um die gesamte Verwendung der Datenbank. Jede Abfrage ist nur über async-Methoden möglich.

Angenommen die Verbindung steht und der Nutzer startet eine Funktion zum Laden irgendwelcher großer Daten aus der DB. Während die Daten laden kann der Nutzer sonstwas machen. Aber woher weiß die App, dass die Daten vollständig geladen wurden und diese angezeigt oder bearbeitet werden können?

Einfaches Beispiel, wieso async/await viel Code spart. (...)

Wenn ich das Beispiel richtig verstehe geht es um die Abarbeitung von Aufgaben deren Ende für den Rest des Programms egal sind. Die Aufgabe "Mail verschicken" kann man z.B. leichter asynchron gestalten als "Mail empfangen". Wann genau die Mail weg ist, ist (theoretisch) egal. Aber wenn man eine Mail empfängt will man auch wissen wann man mit dieser Arbeiten kann.

Warum Dein Code schrecklich ist:
Wenn Du ne Methode hast, die suggeriert, dass sie nur eine Connection durchführt, dann sollte auch dessen Inhalt so sein.

Viel eher sollte Dein Code also so aussehen:

  
public Task ConnectAsync() {  
    return database.OpenAsync();  
 }   
  

Wie gesagt, es war nur ein Beispiel. Man kann das auch allgemein kurz zusammenfassen:

  • Wenn man eine asynchrone Methode ausführt bekommt man das Ergebnis irgendwann später.
  • Interessiert einen das Ergebnis des Aufrufs (oder auch nur die Information, dass der Aufruf komplett ist), muss man irgendwie darüber benachrichtigt werden.
  • Wie geht das mit TPL. Geht das überhaupt mit TPL oder verwendet man dafür ein anderes Pattern?
6.911 Beiträge seit 2009
vor 10 Jahren

Hallo Ares,

Hier werden alle Get-Methode zeitgleich aufgerufen

Aufgerufen werden die Get-Methode auch hier nacheinander und zwar genau in der Reihenfolge wie sie im Code stehen, genauso wird blockierend auf das Ergebnis der Methode gewartet. Aber: Das Ergebnis der Methode ist ein Task bzw. ein Task<T>. Bei await Task.WhenAll(params Task tasks) wird eine Continuation angehängt, die asynchron ausgeführt wird sobald alle übergebenen Tasks fertig sind.

Die "Vereinfachung" die durch async/await kommt ist eine Erleichterung der Programmierung der Continuations. Nehmen wir folgendes Beispiel her, das für die TPL (.net 4.0) passt:


private static Task FooAsync(Task<int> task)
{
	return task.ContinueWith(t =>
	{
		int result = t.Result;
		Console.WriteLine(result);
	});
}

Mit async/await lässt sich der Code umschreiben und zwar so, dass keine explizite Continuation angegeben werden muss:


private static async Task BarAsync(Task<int> task)
{
	int result = await task;
	Console.WriteLine(result);
}

Ein weiterer Vorteil dieser Schreibweise ist, dass der Code näher an der synchronen Variante ist und daher auch lesbarer und einfacher verständlich. Das ändert aber rein gar nichts daran, dass das Verständnis von asynchronen Vorgängen vorhanden sein muss. Daher arbeitet dich bitte gründlich durch Parallel Programming with Microsoft .NET. Erst wenn du das wirklich verstehst, kannst du dir "Task-based Asynchronous Pattern" nochmal anschauen und durcharbeiten.

Bei APM und EAP gäbe es wie gesagt einen Callback oder ein Event, dass entsprechend Bescheid sagen würde. Aber wie läuft das hier?

Eigentlich genau gleich, nur dass statt dem Callback die Continuation, welche vom Compiler aus dem nach await folgenden Code generiert wird, aufgerufen wird.

Wenn man eine asynchrone Methode ausführt bekommt man das Ergebnis irgendwann später.

Kommt darauf an was du als Ergebnis ansiehst. Das Ergebnis in Form eine Task bzw. Task<T> erhälst du unmittelbar. Das Ergebnis, das in der Task<T>.Result-Eigenschaft gehalten wird, erhälst du erst wenn der Task fertig ist. Bei Verwendung von await wird intern quasi auf Task<T>.Result zurückgegriffen.

Interessiert einen das Ergebnis des Aufrufs (oder auch nur die Information, dass der Aufruf komplett ist), muss man irgendwie darüber benachrichtigt werden.

Eben in der Continuation die aufgerufen wird. Schau dir obigen Beispiel-Code an. Geh dort auch mit dem Debugger durch.

Wie geht das mit TPL. Geht das überhaupt mit TPL oder verwendet man dafür ein anderes Pattern?

Siehe auch das Beispiel oben.

Noch zur Info:
Per await wird der Ausführungs-Context des Threads in jenen zurückdelegiert der beim "Aufruf" von await vorhanden war. Daher kann z.B. in GUI-Anwendung ohne explizites Zurückdelegieren auf die UI-Elemente zugegriffen werden. Das in [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) erwähnte passiert hier also unter Haube.
Dies ist aber für Service-Anwendungen nicht nötig, daher kann mittels ConfigureAwait(false) dieses (automatische) Zurückdelegieren unterbunden werden. Z.B.


using (WebClient wc = new WebClient())
{
    string content = await wc.DownloadStringTaskAsync("http://www.mycsharp.de").ConfigureAwait(false);
}

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

A
Ares Themenstarter:in
167 Beiträge seit 2005
vor 10 Jahren

Nochmals vielen Dank für die ausführliche Antwort! Ich denke ich sehe jetzt schon etwas klarer, werde mich aber auf jeden Fall noch weiter in die Beschreibungen vertiefen.

Bei APM und EAP erhält der Aufrufer durch einen Callback oder ein Event die Nachricht, dass die Aufgabe abgearbeitet wurde. Beim TPL läuft statt dessen einfach der Code hinter dem await weiter. Dies geschieht zudem automatisch im Aufruf-Thread, es sei denn man unterbindet dies durch ConfigureAwait(false).

Habe ich das jetzt soweit richtig verstanden?

Es wurde ja schon gesagt, dass async/await viral ist. Dahinter verbirgt sich nichts anderes, als das von mir beobachtete Phänomen, dass ein Einsatz von async/await dazu führt, dass man das quasi durch den kompletten Code bis zum ersten Aufruf durchziehen muss wenn der Aufruf auf das Ergebnis des Task angewiesen ist. Richtig?

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo Ares,

Beim TPL läuft statt dessen einfach der Code hinter dem await weiter.

ich würde eher sagen, der Code hinter dem await wird als Continuation verwendet. Scheinbar einfach ist das nur aus Programmierersicht. Der Compiler muss die Methode mit sehr viel Magie kompilieren, damit das möglich ist. Aus einer Methode mit async/await im Quellcode erstellt der Compiler mindestens zwei Methoden im IL-Code. Aber hinsichtlich der Werte (auch) der (lokalen) Variablen sieht es für den Programmierer bezüglich der folgenden Berechnungen so aus, als würde der Code später nahtlos fortgesetzt werden.

Dies geschieht zudem automatisch im Aufruf-Thread,

Dies geschieht im Aufruf-Thread, sofern der Aufruf-Thread dies ermöglicht. Das ist normalerweise bei GUI-Threads der Fall, bei anderen Thread normalerweise nicht.

In Eleganteste Art aus Worker-Thread auf Controls zugreifen [generell Kontrollfluss zwischen Threads] schrieb ich dazu:

Überhaupt kann man die Ausführung nur an Threads delegieren, die kooperativ sind, also extra so programmiert sind, dass man ihnen Arbeitsaufträge übergeben kann. Wenn ein Thread von Anfang bis Ende eine sequentielle Verarbeitung durchführt, dann tut er eben stur genau das und nicht das, was andere Threads gerne von ihm hätten. So kann man z.B. an den Main-Thread eines Konsolenprogramms üblicherweise nichts delegieren. Spätestens hier scheitert also selbst die SynchronizationContext-Klasse ...

... und eben auch die Task-Klasse bzw. der async-await-Mechanismus.

Du kannst aber bitte nicht jedes Detail nachfragen. Soviel Eigeninitiative erwarten wir schon.

herbivore

J
641 Beiträge seit 2007
vor 10 Jahren

Dies geschieht im Aufruf-Thread, sofern der Aufruf-Thread dies ermöglicht. Das ist normalerweise bei GUI-Threads der Fall, bei anderen Thread normalerweise nicht.

dann brauche Ich doch das ConfigureAwait(false) in Service Anwendungen nicht (weil gfoidl das geschrieben hat).Das brauch Ich doch dann eher wenn Ich es in der GUI unterbinden will, oder?

cSharp Projekte : https://github.com/jogibear9988

49.485 Beiträge seit 2005
vor 10 Jahren

Hallo jogibear9988,

Parameter continueOnCapturedContext: true, um zu versuchen, die Fortsetzung zurück auf den ursprünglichen Kontext zu marshallen; andernfalls false.

ConfigureAwait mit false unterbindet also schon den Versuch, kann also auch dann als sinnvoll angesehen werden, wenn der Versuch sowieso scheitern würde. Einen echten Unterschied macht es aber in der Tat nur dann, wenn ein Zurückdelegieren überhaupt möglich wäre, denn nur dann könnte der Versuch erfolgreich sein.

herbivore

6.911 Beiträge seit 2009
vor 10 Jahren

Hallo jogibear9988,

dann brauche Ich doch das ConfigureAwait(false) in Service Anwendungen nicht (weil gfoidl das geschrieben hat).

Ganz so trivial ist die Sache nicht, aber in Service-Anwendungen ist es i.d.R. besser ConfigureAwait(false) zu verwenden.
Für Hintergründe warum schau dir ExecutionContext vs SynchronizationContext und Consuming the Task-based Asynchronous Pattern Abschnitt "Configuring Suspension and Resumption with Yield and ConfigureAwait" an.

...wenn Ich es in der GUI unterbinden will, oder?

Ja, das ist ein weiterer Anwendungsfall.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

A
Ares Themenstarter:in
167 Beiträge seit 2005
vor 10 Jahren

Noch eine Nachfrage:
Es geht um den korrekten Aufruf einer async Methode auf deren Ende nicht gewartet werden soll. Hierbei gibt der Compiler eine Warnung aus:

Fehlermeldung:
Da auf diesen Aufruf nicht gewartet wird, wird die Ausführung der aktuellen Methode vor Abschluss des Aufrufs fortgesetzt. Ziehen Sie ein Anwenden des 'Await'-Operators auf das Ergebnis des Aufrufs in Betracht.

Beispiel: Ein Objekt DataCenter lädt bei der Erstellung Datensätze über Personen aus einer Datenbank. Das Laden dauert eine Weile und wird daher in einem separaten Thread erledigt. Das DataCenter Object kann schon vorher verwendet werden und wartet daher nicht auf das Laden der Daten:


DataCenter dataCenter = new DataCenter();

... 
public class DataCenter {
   public DataCenter() {
      ...
      State = DataCenterState.NotLoaded;
      LoadPersonsAsync();  // <-- Compiler Warnung
   }

   private async Task LoadPersonsAsync {
      State = DataCenterState.Loading;

      List<Person> = await LoadFromDBAsync(...);
      ...
      State = DataCenterState.Loaded;
   }
}

Die Warnung ist natürlich richtig (DataCenter() wartet nicht und die Bearbeitung wird fortgesetzt). Das ist an der Stelle aber durchaus gewünscht. Kann man die Warnung irgendwie deaktivieren? Oder verwende ich das TAP hier falsch?

6.911 Beiträge seit 2009
vor 10 Jahren

Hallo Ares,

wenn du ein "Fire and Forget" haben willst, so deklariere die Methode wie folgt:


private async void LoadPersonsAsync()
{
    ...
}

Also mit void anstatt Task als Rückgabewert. Auf die gleiche Weise werden auch Eventhandler von GUI-Aktionen für asynchrone Vorgänge deklariert.

Was machst du aber im konkreten Beispiel mit dem Ergebnis der DB-Abfrage, also mit List<Person>? Klar, du zeigst nur einen Code-Auschnitt, aber so geht das Ergebnis irgendwie unter. Ohne dass ich die genauen Anforderungen an diese Klasse kenne, würde ich sie eher so implementieren:


public class DataCenter
{
	private readonly IPersonRepository _personRepository;

	public DataCenterState State
	{
		get
		{
			if (this.PersonLoadTask == null) 
				return DataCenterState.NotLoaded;

			return this.PersonLoadTask.Status == TaskStatus.RanToCompletion ? 
				DataCenterState.Loaded : 
				DataCenterState.Loading;
		}
	}

	public Task<IList<Person>> PersonLoadTask { get; private set; }

	public DataCenter(IPersonRepository personRepository)
	{
		_personRepository = personRepository;
		this.PersonLoadTask = _personRepository.GetPersons();			
	}
}

public interface IPersonRepository
{
	Task<IList<Person>> GetPersons();
}

Das TAP (async/await) ist hier nicht nötig. Entscheidend ist, dass das Ergebnis der DB-Abfrage in einem Task<T> gespeichert wird. Über diesen Task lässt sich auf den DataCenterState schließen - siehe Eigenschaft State.

Bei der Verwendung der Klasse kann dann das TAP ins Spiel kommen:


IPersonRepository personRepository = new PersonRepository();
DataCenter dataCenter = new DataCenter(personRepository);

IList<Person> persons = await dataCenter.PersonLoadTask;

Der Kern des Ganzen ist also Task bzw. Task<T>.
TAP (async/await) ist lediglich eine Erleichterung für Continuations, da der Compiler diesen Code für dich generiert. Wenn keine Continuation nötig ist, so ist auch async/await nicht nötig.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

A
Ares Themenstarter:in
167 Beiträge seit 2005
vor 10 Jahren

Super. Ganz herzlichen Dank!