zu lösen Probleme wie diese haben wir verwendet AsyncLock
vorherige Projekte. Die AsyncLock
wartet, bis die vorherige Sperre aufgehoben wurde.
Die AsyncLock
mag zuerst ein wenig kompliziert erscheinen, aber ich hoffe, dass die mitgelieferten Anwendungsbeispiele sein Verhalten veranschaulichen.
public class AsyncLock
{
private TaskCompletionSource<object> _lastSection;
public AsyncLock()
{
_lastSection = new TaskCompletionSource<object>();
_lastSection.SetResult(null);
}
public class ReleaseLock : IDisposable
{
private readonly TaskCompletionSource<object> _tcs;
public ReleaseLock(TaskCompletionSource<object> tcs)
{
_tcs = tcs;
}
public void Dispose()
{
_tcs.SetResult(null);
}
}
/// <summary>
/// Enters and locks a critical section as soon as the currently executing task has left the section.
/// The critical section is locked until the returned <see cref="IDisposable"/> gets disposed.
/// </summary>
public Task<ReleaseLock> EnterAsync()
{
var newTcs = new TaskCompletionSource<object>();
var toAwait = Interlocked.Exchange(ref _lastSection, newTcs);
return toAwait.Task.ContinueWith((_) => new ReleaseLock(newTcs), TaskContinuationOptions.ExecuteSynchronously);
}
}
Sie können dann await AsyncLock.EnterAsync()
warten verwenden, bis eine frühere Sperre freigegeben wurde. In der EnterAsync
Warteschlange wir die nächste Task
nach der aktuellen Task
mit ContinueWith
. Dies bedeutet, dass die await AsyncLock.EnterAsync()
ausgeführt wird, nachdem die vorherige beendet wurde. Hier
using (await _lock.EnterAsync())
{
// ...
}
ist ein Anwendungsbeispiel:
class Program
{
private static readonly AsyncLock _lock = new AsyncLock();
private static async Task Test(int i, Task toComplete)
{
using (await _lock.EnterAsync())
{
await toComplete;
Console.WriteLine(i);
}
}
public static void Main(string[] args)
{
var tcs1 = new TaskCompletionSource<object>();
var tcs2 = new TaskCompletionSource<object>();
Task.Run(async() =>
{
var t1 = Test(1, tcs1.Task); // start first task
var t2 = Test(2, tcs2.Task); // start second task
tcs2.SetResult(null); // finish second first
tcs1.SetResult(null); // fiish last task
await Task.WhenAll(t1, t2); // will print: 1 and then 2
}).Wait();
}
}
Die Test
Methode nimmt zunächst die Async
Sperre eingeben, warten dann die Aufgabe toComplete
und dann auf die Konsole schreiben.
Wir beginnen zwei Test
Aufgaben ("1" und "2") und die zweite toComplete
erste abzuschließen. Ohne das AsyncLock
druckt das vorherige Beispiel: "2", "1". Mit der AsyncLock
werden die Aufgaben jedoch in der Reihenfolge bearbeitet, in der sie gestartet wurden.
BEMERKUNGEN: Eine letzte Bemerkung. Dies wird Ihre Bearbeitungsreihenfolge erreichen, kann aber manchmal schwierig sein. Wenn Sie solche Sperren verwenden, kann dies leicht zu Deadlocks führen, die schwer zu lösen und schwer zu finden sind. Verwenden Sie Schlösser sehr vorsichtig.
EDIT: Hier ein Anwendungsbeispiel Sie Ihr Problem:
private readonly AsyncLock _lock = new AsyncLock();
public Textbox_TextChangedEvent()
{
GetStocks(texboxText); // every call is now "queued" after the previous one
}
public async Task GetStocks(string texboxText)
{
using(await _lock.EnterAsync())
{
IsBusy = true;
await Task.Run(() => { CreateCollection(texboxText); });
IsBusy = false;
}
}
Wie ist das gewünschte Verhalten anders als das aktuelle Verhalten? –
Welchen Rahmen verwenden Sie? Ist das eine Desktop-App oder eine Web-App? – Michael
aaah Wenn Sie die Arbeit anstehen möchten, verwenden Sie eine Warteschlange, um die auszuführende Arbeit zu speichern, und bearbeiten Sie sie dann in der Reihenfolge – user230910