Laden...

Wie mache ich einen Task zum Arbeiten im Hintergrund asynchron?

Erstellt von Sindelfinger vor 3 Jahren Letzter Beitrag vor 3 Jahren 680 Views
S
Sindelfinger Themenstarter:in
39 Beiträge seit 2019
vor 3 Jahren
Wie mache ich einen Task zum Arbeiten im Hintergrund asynchron?

Liebes Forum,

ich experimentiere seit einigen Stunden nun mit Asynchronen Tasks herum, aber es scheint mir nicht zu gelingen.

Aufgabe ist es, 10000 Positionen als String zu kreieren und diese auszugeben (erstmal zum Verstehen des Ganzen). Darüberhinaus habe ich noch eine ProgressBar eingebaut, um den Fortschritt erkennen zu können.

Mein Aufruf im MainWindow:


 private async void  CreatePosesAsync(object sender, RoutedEventArgs e)
        {
            TBouput.Clear(); // Eine einfache Textbox zum Anzeigen

            Progress<ProgressReportModel> progress = new Progress<ProgressReportModel>();
            progress.ProgressChanged += ReportProgress;

            var watch = System.Diagnostics.Stopwatch.StartNew();

            var results = await AsynchMethods.CreateNewPosesAsync(progress);

            watch.Stop();
            var ElapsedMs = watch.ElapsedMilliseconds;

            TBouput.Text += $"Zeit benötigt: {ElapsedMs} ms";

        }

        private void ReportProgress(object sender, ProgressReportModel e)
        {
            CreateProgress.Value = e.PercentageComplete;
            PrintResults(e.CreatedPos);
        }

        private void PrintResults(string results)
        {
            
                TBouput.Text += results;

        }

Hier rufe ich folgende Methode auf:


   public static async Task<List<string>> CreateNewPosesAsync(IProgress<ProgressReportModel> progress)
        {
  
            List<string> output = new List<string>();
            List<int> Numbers = CreateArray();

            ProgressReportModel report = new ProgressReportModel();

            await Task.Run(() =>
            {
                Parallel.ForEach<int>(Numbers, async (number) =>
               {
                   String results = await  CreatePosesAsync(number);
                   output.Add(results);

                   report.CreatedPos=results;// = output;
                   report.PercentageComplete = Numbers.IndexOf(number)*100 / Numbers.Count();
                   progress.Report(report);
               });
            });

            return output;
        }
        private static async Task<string> CreatePosesAsync(int welche)
        {
            Console.WriteLine($"{welche} {Environment.NewLine}");

            Coord retval = new Coord();
            retval.X = (float)(welche);
            retval.Y = 0;
            retval.Z = 0;

            string s = $"X {retval.X},Y {retval.Y},Z {retval.Z} {Environment.NewLine}";
            
            // Diese Zeile ist wahrscheinlich falsch
            await Task.CompletedTask;
            // aber was muss dort wirklich hin?

            return s;

        }

Für die Progressbar habe ich folgendes ReportModel erstellt:


    public class ProgressReportModel
    {
        public int PercentageComplete { get; set; } = 0;
        public string CreatedPos { get; set; }        
    }

Ich habe schon einiges durchprobiert, aber es bleibt stets asynchron (also das Windows lässt sich in der Zeit nicht verschieben). Online finden sich ja einige Beispiele, aber die machen mir nicht den Eindruck, als wenn sie in meine Aufgabe hineinpassen, da "Sleep" ja nicht unbedingt zur Geschwindigkeit beiträgt.

Mein Endziel bei der ganzen Geschichte ist, später dann kartesische Koordinaten (und zwar verdammt viele) auf geänderte Bezugsframes umzurechnen. Dieser Teil funktioniert bereits (ist ja auch relativ einfache Mathematik), aber vor dem Fliegen haben die Götter ja bekanntlich das Laufen gesetzt (Gesetz bei Natur Daniel San - nicht Miyagi machen 😉)

2 stupid 4 chess? No way.
2 stupid 4 C#? It seems so X(

T
2.219 Beiträge seit 2008
vor 3 Jahren

Der Code sieht ziemlich abenteuerich aus.


// Richt nach WPF, da wäre dein aktueller Ansatz vermutlich besser zu lösen als über ein Event wie bei WinForms.
RoutedEventArgs

// Methode klingt nach Array, liefert aber List<T>, was liefert diese?
// Kann dies nicht z.B. durch Parallel.For abgelöst werden?
List<int> Numbers = CreateArray();

// Anstelle von ProgressReportModel würde ich es korrekt als ProgressReportEventArgs übergeben
// Die Klasse sollte dann auch korrekt von EventArgs abgeleitet sein.
private void ReportProgress(object sender, ProgressReportModel e)

// Zuweisung von 0 ist unnötig, da int per Default 0 ist
public int PercentageComplete { get; set; } = 0;

// Das WriteLine mit NewLine ist redundant, da WriteLine intern auch ein NewLinea anhängt.
Console.WriteLine($"{welche} {Environment.NewLine}");

// Macht in der Methode vermutlich keinen Sinn
// Entweder du lieferst über Task.Run einen neuen Task zurück oder die Methode läuft sonst synchron.
await Task.CompletedTask;

// Zwar ist klar was du erreichen willst, aber schön ist das nicht.
// Die Logik sollte in eine eigene Methode die dann asynchron läuft.
 await Task.Run(() =>
            {
                Parallel.ForEach<int>(Numbers, async (number) =>
               {
                  ...
               });
            });

Vermutlich braucht es noch ein tieferes Verständis für WPF als ich es habe um dir einen besseren Ansatz zu bieten.
Ob der Ansatz über Events so funktioniert kann ich auch nicht sagen.
Vermutlich wärst du mit einem Progressbar mit DataBinding besser dran.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.807 Beiträge seit 2008
vor 3 Jahren

Da sind viele Fehler drin; allen voran, dass aus asynchronem Code, der dann auch noch Parallel ausgeführt (und das nochmals asynchron) auf UI Elemente zugreifst.
Das ist die Basis für aller feinste Race Conditions und Thread-Zugriffsverletzungen.

Ja, die Progress-Infos sollten gebunden werden; async / await ändert nichts daran, dass Du immer mit MVVM arbeiten sollst, wenn Du WPF verwendest.

Asynchroner Code alleine schützt Dich nicht zwangsläufig vor einer blockierenden UI.
Siehe Erklärung in
[FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke)

309 Beiträge seit 2020
vor 3 Jahren

Du könntest das alles vielleicht in eine eigene Klasse auslagern, dann kannst du dich in deim ViewModel an Events hängen und so deine UI aktualisieren lassen. Dann wäre es auch nicht so unübersichtlich wie mit der "AsynchMethods" Utility-Klasse.

S
Sindelfinger Themenstarter:in
39 Beiträge seit 2019
vor 3 Jahren

Der Code sieht ziemlich abenteuerich aus.

// Methode klingt nach Array,

Ganz genau. Später soll eine große Liste von Posen umgerechnet werden. Die sind aber etwas umfangreicher als nur X,Y und Z

// Zuweisung von 0 ist unnötig, da int per Default 0 ist
public int PercentageComplete { get; set; } = 0;
T-Virus

Gewohnheit. Meine Industrieroboter mögen es gar nicht, wenn sie plötzlich auf uninitialisierte Variablen stoßen. Und dann fallen auch mal schnell einige Autos pro Schicht weniger vom Band. Frisst aber kein Brot nehme ich an.

Die Progressbar ist per se erstmal gar nicht so wichtig. Es ging mir nur darum zu visualisieren, ob diese Tasks auch brav ihre Werte rauswerfen, was sie aber nicht tun.

Die von mir erstellte Klasse basiert auf einem Beispielcode aus einem youtube-Video. Dort wird aber Webclient und client.DownloadStringTasksAsync verwendet. Ich brauche einen eigenen Task und möchte diesen eben erstellen.

2 stupid 4 chess? No way.
2 stupid 4 C#? It seems so X(

5.657 Beiträge seit 2006
vor 3 Jahren

Die WebClient-Methoden sind aber schon asynchrone Methoden, die hast du in deinem Beispiel nicht. Du willst Code parallel ausführen. Dafür reicht im einfachsten Fall ein Aufruf von Parallel.ForEach.

Ich empfehle, dir mal die Doku zu Tasks, asynchroner Programmierung und der Parallel-Klasse bzw. PLinq durchzulesen, damit du verstehst, worum es geht. Asynchrone Programmierung und parallele Programmierung sind zwei unterschiedliche Sachen.

Weeks of programming can save you hours of planning

S
Sindelfinger Themenstarter:in
39 Beiträge seit 2019
vor 3 Jahren

Dankeschön für die schnelle Antwort. Hast Du mir auch einen Link dazu? Ich finde nur das mit dem Frühstück

2 stupid 4 chess? No way.
2 stupid 4 C#? It seems so X(

16.807 Beiträge seit 2008
vor 3 Jahren

Dann hast nicht richtig geschaut; in der .NET Basics Doku gibts direkt nen Menüeintrag dafür.
Parallel Processing, Concurrency, and Async Programming in .NET

S
Sindelfinger Themenstarter:in
39 Beiträge seit 2019
vor 3 Jahren

Seit wir in der 13. Klasse Shakespeare im Original gelesen haben hatte ich nicht mehr so viel englischen Text vor der Nase.

Damals gab's aber noch kein Google.

Erstmal Dankeschön - ich kämpfe mich jetzt da durch. Hab bitte ein bißchen Nachsicht mit mir. Mein Hirn ist nach dem Schlaganfall noch nicht auf Prozessgeschwindigkeit und die richtigen Google-Begriffe fallen mir auch noch nicht so schnell ein.

Wir sind ja quasi Nachbarn. Wenn du auf Käsekuchen stehst - hast ihn redlich verdient 😉

2 stupid 4 chess? No way.
2 stupid 4 C#? It seems so X(

16.807 Beiträge seit 2008
vor 3 Jahren

Wir sind ja quasi Nachbarn. Wenn du auf Käsekuchen stehst - hast ihn redlich verdient 😉

Ich nehm den Kaffee; esse aber weder Kuchen noch andere Süßigkeiten 😉

S
Sindelfinger Themenstarter:in
39 Beiträge seit 2019
vor 3 Jahren

Soviel vorweg: OPI HAT'S JETZT KAPIERT 😉

Code wird nachgereicht. Jetzt müssen alte Säcke in's Bett. Ich bereit das morgen auf und poste es dann.

2 stupid 4 chess? No way.
2 stupid 4 C#? It seems so X(