Async: einfaches Visualisieren von Algorithmen

    • VB.NET
    • .NET (FX) 4.5–4.8

      Async: einfaches Visualisieren von Algorithmen

      Das Voranschreiten eines Algorithmus' zu visualisieren war bislang eigentlich unmöglich, denn der Computer kann ja nicht innehalten. Natürlich kann man ihn mit Thread.Sleep() anhalten, aber das hält auch die Oberfläche an - also visualisiert wird dabei auch nix :thumbdown:
      Man hat die Algos immer zur völligen Unkenntlichkeit umstrukturieren müssen, um bestimmte Zustände, die ganz normal im Algo auftreten, als konsistente Objekte verfügbar zu bekommen, die man einerseits im Gui präsentieren kann, von denen man andererseits aber auch den nächsten Schritt des Algos gehen kann. Eine einfache For-Schleife mutiert mindestens zu einem Button, einer Eventhandler-Methode und einer Klassen-Variable - von der Schleife bleibt nichts ;( .

      Aber dank Async kann der Computer nun innehalten, und diese entstellten Mutanten dürfen aussterben - mögen sie ruhen in Frieden :saint:
      Das einfachste Innehalten geht mit Await Task.Delay(500): ein Delay von 500ms einzuschieben.
      Das zweit-einfachste ist, auf ein AutoResetEvent zu warten, welches der User per Button-Click durchschaltet:

      VB.NET-Quellcode

      1. Private _Blocker As New AutoResetEvent(True)
      2. Private Function Wait() As Task
      3. Return Task.Run(Function() _Blocker.WaitOne())
      4. End Function
      5. Private Sub btStep_Click(sender As Object, e As EventArgs) Handles btStep.Click
      6. _Blocker.Set()
      7. End Sub
      Die Wait-Methode (#3) ist ebenso aufzurufen wie Task.Delay, und sie schiebt ebenso einen Delay ein, nur jetzt bestimmt der User, wanns weiter geht.

      Als praktisches Demo habe ich den BubbleSort-Algo hergenommen - hier the whole Story:

      VB.NET-Quellcode

      1. Imports System.ComponentModel
      2. Imports System.Threading
      3. Imports System.Runtime.CompilerServices
      4. Public Class frmSortVisualizerVb
      5. Private _List As New BindingList(Of Integer)()
      6. Private _Blocker As New AutoResetEvent(True)
      7. Public Sub New()
      8. InitializeComponent()
      9. ListBox1.DataSource = _List
      10. End Sub
      11. Private Sub Button_Click(sender As Object, e As EventArgs) Handles btBubbleSort.Click, btReset.Click, btStep.Click
      12. Select Case True
      13. Case sender Is btReset : Generate(30, 30)
      14. Case sender Is btBubbleSort : BubbleSort(_List)
      15. Case sender Is btStep : _Blocker.Set()
      16. End Select
      17. End Sub
      18. Private Sub ckStepwise_CheckedChanged(sender As Object, e As EventArgs) Handles ckStepwise.CheckedChanged
      19. btStep.Enabled = ckStepwise.Checked
      20. _Blocker.[Set]()
      21. End Sub
      22. Private Async Sub Generate(max As Integer, count As Integer)
      23. _List.Clear()
      24. Dim rnd = New Random(0)
      25. For i = 0 To count - 1
      26. If ckStepwise.Checked Then Await Wait()
      27. _List.Add(rnd.[Next](max))
      28. Next
      29. End Sub
      30. Private Async Sub BubbleSort(lst As IList(Of Integer))
      31. 'repeat: run up, compare each element with its successor - if greater: Swap
      32. 'topSwap/lastSwap consider, that the elements behind the last swap-Position of the previous Run-up already are sorted
      33. Dim topSwap = lst.Count - 2
      34. Dim lastSwap As Integer
      35. Do
      36. lastSwap = 0
      37. For i = 0 To topSwap
      38. If lst(i) > lst(i + 1) Then
      39. 'If ckStepwise.Checked Then Await Task.Delay(500)
      40. If ckStepwise.Checked Then Await Wait()
      41. lst.Swap(i, i + 1)
      42. lastSwap = i
      43. End If
      44. Next
      45. topSwap = lastSwap - 1
      46. Loop Until lastSwap = 0
      47. End Sub
      48. Private Function Wait() As Task
      49. Return Task.Run(Function() _Blocker.WaitOne())
      50. End Function
      51. End Class
      52. Friend Module Extensions
      53. <Extension> _
      54. Public Sub Swap(Of T)(Subj As IList(Of T), I1 As Integer, I2 As Integer)
      55. Dim Tmp = Subj(I1)
      56. Subj(I1) = Subj(I2)
      57. Subj(I2) = Tmp
      58. End Sub
      59. End Module
      Eine Listbox, gebunden an eine BindingList(Of Integer) (#7, 12).
      Es gibt die 3 Buttons btReset, btBubbleSort und btStep, eine Checkbox ckStepwise, sowie die beiden Algorithmen Generate und BubbleSort.
      Alles selbsterklärend, wie man sieht: Nomen est Omen!
      Innegehalten wird in Zeilen #32, sowie (#46), #47
      Einzig zu erklären vlt. das AutoResetEvent: Dessen Methode .WaitOne() blockiert den aktuellen Thread so lange, bis von einem anderen Thread aus mit .Set() freigeschaltet wird.
      Auf das erstaunlichste, nämlich den Async/Await-Pattern selbst gehe ich hier nicht ein, weil darauf bin ich hier schon eingehender drauf eingegangen: Async, Await und Task
      Naja - kurz: Am Await-Aufruf bricht eine Methode verfrüht ab, und der Aufrufer fährt fort, ohne blockiert worden zu sein.
      Währenddessen wird das Awaitete in einem Task abgearbeitet. Ist der Task fertig hopft die Methode auf einmal wieder rein, und setzt fort als sei nichts gewesen. 8|
      Klar soweit ?

      same in c#

      C#-Quellcode

      1. public partial class frmSortVisualizerCs : Form {
      2. private BindingList<int> _List = new BindingList<int>();
      3. private AutoResetEvent _Blocker = new AutoResetEvent(true);
      4. public frmSortVisualizerCs() {
      5. InitializeComponent();
      6. listBox1.DataSource = _List;
      7. btStep.Click += (s, e) => _Blocker.Set();
      8. btReset.Click += (s, e) => Generate(30, 30);
      9. btBubbleSort.Click += (s, e) => BubbleSort(_List);
      10. ckStepwise.CheckedChanged += ckStepwise_CheckedChanged;
      11. }
      12. void ckStepwise_CheckedChanged(object sender, EventArgs e) {
      13. btStep.Enabled = ckStepwise.Checked;
      14. _Blocker.Set();
      15. }
      16. private async void Generate(int max, int count) {
      17. _List.Clear();
      18. var rnd = new Random(0);
      19. for (var i = -1; ++i < count; ) {
      20. if (ckStepwise.Checked) await Wait();
      21. _List.Add(rnd.Next(max));
      22. }
      23. }
      24. private async void BubbleSort(IList<int> lst) {
      25. //repeat: run up, compare each element with its successor - if greater: Swap
      26. //topSwap/lastSwap consider, that the elements behind the last swap-Position of the previous Run-up already are sorted
      27. var topSwap = lst.Count - 2;
      28. int lastSwap;
      29. do {
      30. lastSwap = 0;
      31. for (var i = 0; i <= topSwap; ++i) {
      32. if (lst[i] > lst[i + 1]) {
      33. //if (ckStepwise.Checked) await Task.Delay(500);
      34. if (ckStepwise.Checked) await Wait();
      35. lst.Swap(i, i + 1);
      36. lastSwap = i;
      37. }
      38. }
      39. topSwap = lastSwap-1;
      40. } while (lastSwap != 0);
      41. }
      42. private Task Wait() {
      43. return Task.Run(() => _Blocker.WaitOne());
      44. }
      45. }
      46. public static class Extensions {
      47. public static void Swap<T>(this IList<T> Subj, int I1, int I2) {
      48. var Tmp = Subj[I1];
      49. Subj[I1] = Subj[I2];
      50. Subj[I2] = Tmp;
      51. }
      52. }


      Vielen Dank übrigens an @RodFromGermany, @NyuQz und @RushDen, weil die haben mich inspiriert, wie man so sagt, hier und hier :thumbsup:
      Dateien

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