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 ;>
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!"
Danke!
Werde ich morgen genauer ansehen, die Berechnungen sind schon in einer eigenen Klasse.