爲了解決像這樣的問題,我們已經使用了一個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