解決するためにこのような問題は、以前のプロジェクト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
では、Task
の後に次のTask
の後にContinueWith
を使用してキューします。つまり、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
を待って、その後コンソールに書き込みます。
我々は2つのTest
タスク("1"、および"2")を開始し、第一、第二toComplete
を完了します。 AsyncLock
がなければ、前の例では"2","1"が表示されます。 AsyncLock
では、タスクは開始された順序で処理されます。
備考: 1つの最後の発言。これにより処理オーダーが達成されますが、時には面倒なこともあります。このようなロックを使用すると、デッドロックが発生しやすくなります。デッドロックは、解決しにくく、最初に見つけにくいものです。 ロックをよくご使用ください。
編集:ここでは使用例あなたの問題:
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;
}
}
どのように希望する動作は、現在の動作よりも違うのですか? –
あなたはどのフレームワークを使用していますか?これはデスクトップアプリですか、ウェブアプリですか? – Michael
aaah作業をキューに入れたい場合は、キューを使用して作業を保存してから、順番に処理してください。 – user230910