Laden...

Deadlock Task.Result

Erstellt von fungi35 vor 2 Jahren Letzter Beitrag vor 2 Jahren 298 Views
F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 2 Jahren
Deadlock Task.Result

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

4.942 Beiträge seit 2008
vor 2 Jahren

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?

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 2 Jahren

Windows-Forms 🙁

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 2 Jahren

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();
        }

16.842 Beiträge seit 2008
vor 2 Jahren

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.

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 2 Jahren

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?

4.942 Beiträge seit 2008
vor 2 Jahren

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).

16.842 Beiträge seit 2008
vor 2 Jahren

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

  • 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

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.

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 2 Jahren

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.

16.842 Beiträge seit 2008
vor 2 Jahren

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.