Hallo Leute,
beim Speichern von Daten bekomme ich sporadisch einen Deadlock in der Zeile "SaveResult result = task.Result;".
private Task<SaveResult> Save()
{
return Task.Run(() =>
{
SaveResult retVal = new SaveResult();
this.dataprovider.save();
return retVal;
});
}
public void SaveItem()
{
this.ShowWaitingBar();
Task<SaveResult> task = this.Save();
SaveResult result = task.Result;
this.HideWaitingBar();
}
Ich möchte auf das Speichern warten, ohne dass die GUI blockiert. In unser letzten c#-Schulung habe ich gelernt, dass das wie oben umgesetzt die beste Methode wäre. Ich verstehe nicht, warum hier ein Deadlock auftritt.
Gruß fungi
Sorry, aber das ist Murks - so blockierst du ja trotzdem die GUI, s.a. [FAQ] Warum blockiert mein GUI?
Benutze async/await
: Asynchrone Programmierung mit async und await
Verwendest du WinForms oder WPF?
Ich bin gerade total verwirrt. Ich hab' mir die entsprechenden Teile der Doku durchgelesen und den Code geändert.
Kannst Du bitte einmal drüber schauen, ob ich es richtig verstanden habe?
private async Task<SaveResult> SaveAsync()
{
return await Task.Run(() =>
{
SaveResult retVal = new SaveResult();
this.dataprovider.save();
return retVal;
});
}
public async void SaveItem()
{
this.ShowWaitingBar();
SaveResult result = await this.SaveAsync();
this.HideWaitingBar();
}
Implementier Deinen Datenprovider async, dann ist auch das Task.Run nicht mehr notwendig.
async/await ist auch ein Thema, dessen Konzept man verstanden haben musst. Aus Erfahrung weiß ich, dass das nicht in 15 Minuten zu machen ist 😉
Wenn Du das Konzept richtig implementiert hast, dann hast Du am Ende etzwas wie
private async Task<SaveResult> SaveAsync()
{
SaveResult retVal = new SaveResult();
await this.dataprovider.SaveAsync().ConfigureAwait(true);
return retVal;
}
public async void SaveItem() // async void ist nur legitim bei einem Event Handler!
{
this.ShowWaitingBar();
SaveResult result = await this.SaveAsync().ConfigureAwait(true);
this.HideWaitingBar();
}
Task.Run()
braucht man - wenn überhaupt - nur bei CPU bounded work, aber nicht bei asynchroner IO Implementierung.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ja, das hast Du wohl recht. Es ist nur unschön wenn in internen Schulungen falsche Sachverhalte vermittelt werden.
Rein interessehalber: Wie würde ich SaveAsync() innerhalb einer synchronen Methode richtig aufrufen, wenn ich auf await verzichten muss, da die void-Methode nicht als async markiert werden soll?
Ohne async
kannst du dies nicht so implementieren. Du müßtest dann umständlich eigene Ereignisse definieren und darauf dann reagieren.
Oder meinst du explizit async void
? Dann verwende einfach async Task
.
Edit: Nach WinForms oder WPF hatte ich nur deswegen gefragt, weil man bei WPF dies anders lösen würde (MVVM).
@Abt: Warum verwendest du jeweils explizit ConfigureAwait(true)
? So wie ich das u.a. in ConfigureAwait FAQ ("Why would I want to use ConfigureAwait(true)?") gelesen habe, bewirkt das nichts.
Edit2: @fungi35, ich habe auch noch den Artikel .NET Async / Await: Was Sie schon immer über ConfigureAwait wissen wollten - aber nie zu fragen wagten gefunden, der u.a. dein Problem mit Result
aufzeigt (diesen solltest du dann mal deinem Kursleiter zum Lesen geben).
Rein interessehalber: Wie würde ich SaveAsync() innerhalb einer synchronen Methode richtig aufrufen, wenn ich auf await verzichten muss, da die void-Methode nicht als async markiert werden soll?
Warum soll sie das nicht? Die Fragestellung zeigt zwei Dinge
Ansonsten ist das eine durchaus weit verbreitete Fragestellung für (puzzelige) Legacy Anwendungen:
Google Suche nach "c# call async method synchronously"
Die Frage ist daher auch in den Docs beantwortet: https://docs.microsoft.com/de-de/dotnet/csharp/programming-guide/concepts/async/async-return-types
.GetAwaiter().GetResult();
Edit um Th69 zu beantworten:
@Abt: Warum verwendest du jeweils explizit ConfigureAwait(true)? So wie ich das u.a. in ConfigureAwait FAQ ("Why would I want to use ConfigureAwait(true)?") gelesen habe, bewirkt das nichts.
Das ist korrekt. Das Standardverhalten ist ConfigureAwait(true)
, weshalb dies in der UI Implementierung weggelassen werden kann.
Die Idee, wieso ich das explizit genannt habe ist, dass er nachschaut, was dies tut um so zu wissen, dass man den darunterliegenden Code mit ConfigureAwait(false)
aufruft und ConfigureAwait(true)
in der UI, um Deadlocks zu vermeiden.
Das gilt für WinForms, WPF aber auch zB für Blazor.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Warum soll sie das nicht? Die Fragestellung zeigt zwei Dinge
- Wie async/await funktioniert ist unbekannt - es fehlen Grundlagen 🙂
- Das Konzept soll nicht vollständig angewandt werden, was für die Software-Qualität i.d.R. nicht gesund ist
Wir wollen unsere Software von Background-Workern auf das modernere Task-Konzept umstellen. Aktuell sind es nur Testprojekte. Ich sehe, dass da noch viel Schulungs-/Lern-Bedarf ist.
async/await ist ein durchgängiges Konzept und wenn man es richtig machen will und wirklich Benefits davon haben will, dann muss man das durch alle Anwendungsschichten ziehen.
Eine asynchrone Methode synchron auszuführen untergräbt das ganze Konstrukt 😉
Es ist aber keine Raketenwissenschaft.
Wenn man sich nen paar Stunden hinsetzt oder mal nen Training einkauft, dann ist das alles schnell machbar.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code