Async Verständnis

  • Allgemein

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von Telcrome.

    Async Verständnis

    Also in .net 4.5 war MS ja so freundlich, das ganze mit den Threads zu vereinfachen (deren Meinung auf ihren Artikeln, was neu hinzugekommen ist in 4.5)

    Jetzt versuchte ich erstmal eine kleine Pause in eine Methode zu integrieren. Ohne, dass der UI-Thread blockiert wird.
    Da haben sie ihr Wort auch gehalten:

    VB.NET-Quellcode

    1. private async void button2_Click(object sender, EventArgs e)
    2. {
    3. await Task.Delay(2000); //Funktioniert wunderbar
    4. }


    Doch jetzt wollte ich dieses Feature für eine eigene Methode versuchen, das blockiert jedoch ohne jede Scham:
    (Einfach nur eine Schleife, die sehr lange einen Integer hochzählt)

    VB.NET-Quellcode

    1. private async void button1_Click(object sender, EventArgs e)
    2. { //Das ist die Methode vom Button, den ich zum aufrufen drücke
    3. bool Ergebnis = await Hochrechnen();
    4. MessageBox.Show(Ergebnis.ToString());
    5. }
    6. public async Task<bool> Hochrechnen()
    7. {
    8. Int32 Wert = 0;
    9. //Während dieser Schleife kann man im Programm nichts mehr machen
    10. for (int i = 0; i < 2147000000; i++)
    11. {
    12. Wert += 1;
    13. }
    14. return await Task.FromResult<bool>(true);
    15. }


    Was habe ich jetzt daran falsch verstanden? Also mein Ziel ist, dass ich eine blockierende Methode (wie die mit der Schleife) aufrufen kann, ohne, dass mein Programm blockiert wird...
    Das ist nicht ganz richtig. Man sollte wissen, was der Compiler aus Async-Methoden macht, damit man das Verhalten vernünftig versteht. Sonst macht man einfach irgendetwas und man hat irreversiblen "Clusterfuck" produziert.

    Hieraus:
    Spoiler anzeigen

    C-Quellcode

    1. private async void testVoid()
    2. {
    3. Console.WriteLine("abc");
    4. await Task.Delay(2000); //Funktioniert wunderbar
    5. Console.WriteLine("123");
    6. }

    Macht der Compiler sinngemäß (die finale Umsetzung ist natürlich etwas komplizierter und umfangreicher) sowas hier:
    Spoiler anzeigen

    C-Quellcode

    1. private void testVoid()
    2. {
    3. Console.WriteLine("abc");
    4. (new Task(() => {
    5. Thread.Sleep(2000); // (Task.Delay verwendet natürlich nicht Thread.Sleep, ich verwende das hier aber mal.. weil man es versteht)
    6. testVoid_AfterAwait();
    7. })).Start();
    8. }
    9. private void testVoid_AfterAwait()
    10. {
    11. Console.WriteLine("123");
    12. //Achtung: Hier erfolgt der Callback in einem anderen Threadkontext, sodass es bei Zugriffen auf andere Threads krachen kann! (Ja, Tasks können auch synchron ausgeführt werden)
    13. //Beim TAP wird dies mit diversen Methoden umgangen, sodass der Callback im gleichen Kontext erfolgt wie die Ausgangsmethode. Das kann man aber auch ausschalten, wenn es zum Beispiel unnötig ist.
    14. }


    Wenn du eine eigene asynchrone Methode implementieren willst, musst diese gar nicht async sein. Das kann zum Beispiel so aussehen:
    Spoiler anzeigen

    C-Quellcode

    1. public Task<bool> Hochrechnen()
    2. {
    3. return new Task<Bool>(() => {
    4. int wert = 0;
    5. for (int i = 0; i < int.MaxValue; ++i)
    6. wert += 1;
    7. return true;
    8. });
    9. }
    Verwenden tust du das dann mit bool result = await Hochrechnen();.

    Wie man eine asynchrone Methode erstellt, hängt immer davon ab, was du machen willst. So, wie ich oben geschrieben habe "geht" es immer. Das Problem dabei ist aber, dass dabei keinerlei Ressourcen eingespart werden, sondern das Ganze einfach nur in einem anderen Thread läuft. Der Sinn hinter dem TAP liegt aber darin, dass Ressourcen freigegeben werden (genau wie beim APM-Pattern). Das Thema wird auf diesem MSDN-Blogartikel ncoheinmal aufgeriffen:
    blogs.msdn.com/b/pfxteam/archive/2012/03/24/10287244.aspx.
    Normalerweise sollte man immer mit der TaskCompletionSource arbeiten. Deshalb soll man dieses:
    Spoiler anzeigen

    C-Quellcode

    1. public Task SleepAsync(int millisecondsTimeout)
    2. {
    3. TaskCompletionSource<bool> tcs = null;
    4. var t = new Timer(delegate { tcs.TrySetResult(true); }, null, –1, -1);
    5. tcs = new TaskCompletionSource<bool>(t);
    6. t.Change(millisecondsTimeout, -1);
    7. return tcs.Task;
    8. }
    auch diesem vorziehen:
    Spoiler anzeigen

    C-Quellcode

    1. public Task SleepAsync(int millisecondsTimeout)
    2. {
    3. return Task.Run(() => Thread.Sleep(millisecondsTimeout));
    4. }

    Beide machen das Gleiche. Warum aber die obere, offensichtlich kompliziertere vorzeiehen? Weil die untere während der ganzen Schlafzeit einen Thread komplett blockiert. Die obere Implementierung tut das nicht. Diese gibt den Thread frei, während "gewartet" wird. Mehr dazu gibt es in dem oben schon verlinkten Blogeintrag auf MSDN.


    Hier noch was von Channel9 zu Async: channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-829T
    Hier noch nen Worddokument dazu: microsoft.com/en-us/download/details.aspx?id=19957
    Und hier ein weiterer Blogartikel, der sich mit dem Thema befasst: blog.stephencleary.com/2012/02/async-and-await.html
    Von meinem iPhone gesendet

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „nikeee13“ ()

    Okay, dann werden die Methoden, die man so aufruft:

    VB.NET-Quellcode

    1. await Methode();

    Also so geschrieben?:

    VB.NET-Quellcode

    1. public Task<Point> LocationOfBitmap(Bitmap GrossBitmap, Bitmap KleinBitmap, Int32 GENAUIGKEIT)
    2. {
    3. return Task.Run(() =>
    4. {
    5. //Lange Aktion
    6. return BesterPunkt;
    7. }, new CancellationToken(false));
    8. }

    So funktioniert es zumindest schonmal für mein spezielles Problem. Um die Effizienz usw. hab ich mich dabei noch nicht gekümmert.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Telcrome“ ()