为了解决像这样的问题,我们已经使用了一个AsyncLock
以前的项目。 AsyncLock
将等到上一次锁定被释放。
AsyncLock
可能看起来有点复杂,但我希望提供的用法示例将说明其行为。
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);
}
}
然后您可以使用await AsyncLock.EnterAsync()
等待,直到任何以前的锁被释放。在EnterAsync
中,我们使用ContinueWith
在当前的Task
之后排列下一个Task
。这意味着await AsyncLock.EnterAsync()
将在前一个完成后执行。
using (await _lock.EnterAsync())
{
// ...
}
下面是一个使用例如:
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();
}
}
的Test
方法采用将第一输入Async
锁,然后等待任务toComplete
,然后写到控制台。
我们启动两个Test
任务(“1”,并“2”),并完成第二toComplete
第一。没有AsyncLock
前面的示例打印:“2”,“1”。但是,使用AsyncLock
时,任务按照它们开始的顺序进行处理。
备注:最后一句话。这将实现您的处理顺序,但有时可能会非常棘手。使用这样的锁可能很容易导致死锁,这些死锁很难解决,并且首先难以找到。 使用非常小心锁定。
编辑:这里使用示例您您的问题:
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;
}
}
怎么比当前行为的不同期望的行为? –
你使用的是什么框架?这是一个桌面应用程序或一个Web应用程序? – Michael
aaah如果你想排队工作,使用队列来存储要完成的工作,然后按顺序处理它 – user230910