Laden...

async / await auf einen (aktiven) Task begrenzen

Erstellt von Gimmick vor 6 Jahren Letzter Beitrag vor 6 Jahren 1.467 Views
G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 6 Jahren
async / await auf einen (aktiven) Task begrenzen

Hallo,

ich habe eine Frage zu Async/Await.
Und zwar rufe ich über ein trackbar_Scroll-Event eine async-void-Methode auf, in welcher ich auf eine Berechnung warte.


 private void trackBar_Scroll(object sender, EventArgs e)
        {
            if (listView_Intervalle.SelectedItems.Count > 0)
            {
                Update();
            }
        }

private async void Update()
          {
              List<int> Intervalle = new List<int>();
              List<Color> IntervallFarben = new List<Color>();
              .....

              Result = await Task.Run(() => DataDisplay.Gradient(Intervalle, IntervallFarben, checkBox_Raster.Checked));
          }

Wenn ich an der Trackbar schiebe wird für jeden trackbar-Wert eine Berechnung gestartet.
Aber wie ändere ich es so ab, dass nur ein Task asynchron läuft und die neuen Tasks, bis auf den neusten, verworfen werden?
Sodass der letzte Input auf jedenfall noch ausgeführt wird, aber nicht für jeden Trackbar-Wert losgerechnet wird.

Habe auch schon probiert den Event-Handler asynchron zu setzen und auf die Update()-Methode zu warten, dann funktioniert das zwar, aber der Schieberegler ruckelt - der soll aber fluffig gleiten ;>

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo Gimmick,

dass nur ein Task asynchron läuft und die neuen Tasks, bis auf den neusten, verworfen werden?

speichere die Tasks in eine Liste*. Brich alle Tasks, außer den neuesten ab -> Cancellation mit der CancellationTokenSource.

* bzw. wenn es eh nur einen aktiven Task geben darf/kann, so reicht ein Feld aus.


using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private readonly IMyWorker      _myWorker;
        private Task<Result>            _activeTask;
        private CancellationTokenSource _cts = new CancellationTokenSource();

        public Form1(IMyWorker myWorker = null)
        {
            InitializeComponent();

            _myWorker = myWorker ?? new MyWorker();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.UpdateAsync();
        }

        private async void UpdateAsync()
        {
            if (_activeTask != null)
            {
                _cts.Cancel();
                _cts = new CancellationTokenSource();
            }

            _activeTask = _myWorker.UpdateAsync(_cts.Token);

            try
            {
                Result res = await _activeTask;

                label1.Text = res.ToString();
            }
            catch (OperationCanceledException) { }
        }
    }

    public class Result
    {
        public int Value { get; }

        public Result(int value) => this.Value = value;

        public static implicit operator Result(int i)      => new Result(i);
        public static implicit operator int(Result result) => result.Value;

        public override string ToString() => this.Value.ToString();
    }

    public interface IMyWorker
    {
        Task<Result> UpdateAsync(CancellationToken cancellationToken = default);
    }

    public class MyWorker : IMyWorker
    {
        private static int      _counter;
        private readonly Random _random = new Random();

        public async Task<Result> UpdateAsync(CancellationToken cancellationToken = default)
        {
            int i = _counter++;

            // Das lässt sich besser umsetzen ;-)

            cancellationToken.ThrowIfCancellationRequested();

            await Task.Delay(_random.Next(500, 1000));

            cancellationToken.ThrowIfCancellationRequested();

            return i;
        }
    }
}

Anmerkung: Das Ganze funktioniert, da sich in Result res = await _activeTask; das await nicht auf den _activeTask bezieht, sondern auf den TaskAwaiter<T> (ein Werttyp), der im Hintergrund durch die Compiler-Magic erstellt wird. Und dieser TaskAwaiter "treibt" die vom Compiler erstellte asynchrone Statusmaschine an. Daher kann der _activeTask ruhig gegen einen anderen ausgetauscht werden.

BTW: lagere die Berechnung od. was das ist in eine eigene Klasse aus.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

G
Gimmick Themenstarter:in
154 Beiträge seit 2015
vor 6 Jahren

Danke!
Werde ich morgen genauer ansehen, die Berechnungen sind schon in einer eigenen Klasse.