C# 如何确保异步任务方法调用同步执行
嗨,我有文本框,它有文本更改事件。每次在文本框中插入字符时,都会触发文本更改事件。文本更改事件调用异步任务方法。下面是我的事件和异步任务方法C# 如何确保异步任务方法调用同步执行,c#,multithreading,async-await,task,task-parallel-library,C#,Multithreading,Async Await,Task,Task Parallel Library,嗨,我有文本框,它有文本更改事件。每次在文本框中插入字符时,都会触发文本更改事件。文本更改事件调用异步任务方法。下面是我的事件和异步任务方法 public Textbox_TextChangedEvent() { GetStocks(texboxText); } public async Task GetStocks(string texboxText) { IsBusy = true; await Task.Run(()
public Textbox_TextChangedEvent()
{
GetStocks(texboxText);
}
public async Task GetStocks(string texboxText)
{
IsBusy = true;
await Task.Run(() => { CreateCollection(texboxText); });
IsBusy = false;
}
问题
如何确保一个接一个地同步调用GetStocks方法
示例
假设用户将inputTed
作为输入文本。然后我希望异步调用一个接一个地完成。
i、 e它应该按照下面的顺序调用GetStocks,并按照下面的顺序自己完成任务
根据具体情况,一个简单的选择可能是:
public async Task GetStocks(string texboxText)
{
Task.Run(() => {
IsBusy = true;
CreateCollection(texboxText);
IsBusy = false;
});
}
为了解决这样的问题,我们在以前的项目中使用了
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);
}
}
下面是一个使用示例:
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();
}
}
期望的行为与当前的行为有何不同?您使用的是什么框架?这是桌面应用程序还是web应用程序?啊,如果您想将工作排队,请使用队列存储要完成的工作,然后按顺序处理。我正在使用.Net framework和WPF应用程序。抱歉,上面的代码对我不起作用。实际上,我在这里面临的问题是,按照我问题中提到的顺序,进行了三个异步调用,1.GetStocks(T)2.GetStocks(Te)3.GetStocks(Ted)。但是我得到结果的顺序是3,2,1。但我希望结果按以下顺序返回1,2,3,这很奇怪
ContinueWith
insideEnterAsync
。作为状态传入newTcs
,却被代理忽略?这感觉就像是试图避免关闭中途放弃的局部变量。提醒我这个AsyncLock
版本(除了这个版本实际上使用状态):@KirillShlenskiy你是对的。从旧的Silverlight项目获取代码,其中此参数不是可选的。将更新答案。
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();
}
}
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;
}
}