C# 激发多个异步任务并仅更新来自最新任务的结果
我一直在修补Blazor,遇到了一个异步/等待问题。我添加了一个搜索输入字段,它触发一个异步操作,当用户开始键入时,该操作会将搜索结果返回给用户。问题是异步任务以错误的顺序完成。激发的第一个任务返回并使已完成的后续任务的结果无效 我想出了一个解决方案,包括跟踪任务,但这是正确的做法还是我走错了方向 我当前的解决方案如下所示:C# 激发多个异步任务并仅更新来自最新任务的结果,c#,async-await,blazor,C#,Async Await,Blazor,我一直在修补Blazor,遇到了一个异步/等待问题。我添加了一个搜索输入字段,它触发一个异步操作,当用户开始键入时,该操作会将搜索结果返回给用户。问题是异步任务以错误的顺序完成。激发的第一个任务返回并使已完成的后续任务的结果无效 我想出了一个解决方案,包括跟踪任务,但这是正确的做法还是我走错了方向 我当前的解决方案如下所示: private List<Task> runningTasks = new List<Task>(); prote
private List<Task> runningTasks = new List<Task>();
protected async Task OnSearchTermChanged(ChangeEventArgs e)
{
SearchTerm = e.Value.ToString();
if(SearchTerm.Length >= 3)
{
SearchResult = null;
var task = AdService.SearchUsers(SearchTerm);
runningTasks.Add(task);
var result = await task;
if(task == runningTasks.Last())
{
SearchResult = result;
}
}
else
{
SearchResult ??= new List<AdUser>();
SearchResult.Clear();
}
}
一般来说,异步操作有顺序和并行组合。如果你想让事情按照你真正想要的顺序发生,你就不能并行运行。请参阅下面的代码:
var enumerableOfTasks = ... // whatever enumerable of tasks in some desired order
foreach (var task in enumerableOfTasks) await task;
…所有的魔法都发生在等待任务点。除非当前任务完成,否则它不会让下一个任务执行。这可以保证最后执行的任务不会由于竞争条件而被某个前置任务覆盖。通常来说,异步操作有顺序和并行组合。如果你想让事情按照你真正想要的顺序发生,你就不能并行运行。请参阅下面的代码:
var enumerableOfTasks = ... // whatever enumerable of tasks in some desired order
foreach (var task in enumerableOfTasks) await task;
…所有的魔法都发生在等待任务点。除非当前任务完成,否则它不会让下一个任务执行。这可以保证最后执行的任务不会因为竞争条件而被某个前置任务覆盖。我在这种情况下使用我称之为异步上下文的方法,这可能是一个非常糟糕的名称,因为上下文现在可能意味着很多事情。您只需要某种唯一的值,新对象就足够了,在等待之前将该值捕获到局部变量中,然后在等待之后对其进行比较。如果它们不匹配,那么代码知道它不再是当前代码
private object _context;
protected async Task OnSearchTermChanged(ChangeEventArgs e)
{
SearchTerm = e.Value.ToString();
if (SearchTerm.Length >= 3)
{
var localContext = _context = new object();
SearchResult = null;
var task = AdService.SearchUsers(SearchTerm);
var result = await task;
if (localContext == _context)
{
SearchResult = result;
}
}
else
{
_context = null;
}
}
我在这种场景中使用了我称之为异步上下文的东西,这可能是一个非常糟糕的名字,因为上下文现在可能意味着很多事情。您只需要某种唯一的值,新对象就足够了,在等待之前将该值捕获到局部变量中,然后在等待之后对其进行比较。如果它们不匹配,那么代码知道它不再是当前代码
private object _context;
protected async Task OnSearchTermChanged(ChangeEventArgs e)
{
SearchTerm = e.Value.ToString();
if (SearchTerm.Length >= 3)
{
var localContext = _context = new object();
SearchResult = null;
var task = AdService.SearchUsers(SearchTerm);
var result = await task;
if (localContext == _context)
{
SearchResult = result;
}
}
else
{
_context = null;
}
}
你怎么知道一项任务是最后一项?当用户键入任务时,是否会添加更多的任务?我相信您的方法是正确的-您确实需要跟踪任务并检查刚刚完成的任务是否是最新的任务。我只能建议只存储上次运行的任务,而不是整个列表。在开始新的任务之前,你也可以考虑取消以前的任务。你真正想要实现的是什么?最后一个被激发的查询结果总是很重要的-因此可以删除较早的查询结果,或者您需要所有被激发请求的结果?我相信这会对您有所帮助。Task.Wheall在所有其他任务完成后返回一个任务,因此您可以等待,您也可以有一个计时器,因此可能在更改按键后有0.5到1秒的延迟,然后再对其执行任何操作,然后,如果用户连续快速键入10个字母,则事件只针对最后一个字母触发,而不是10次。您如何知道任务是最后一个?当用户键入任务时,是否会添加更多的任务?我相信您的方法是正确的-您确实需要跟踪任务并检查刚刚完成的任务是否是最新的任务。我只能建议只存储上次运行的任务,而不是整个列表。在开始新的任务之前,你也可以考虑取消以前的任务。你真正想要实现的是什么?最后一个被激发的查询结果总是很重要的-因此可以删除较早的查询结果,或者您需要所有被激发请求的结果?我相信这会对您有所帮助。Task.Wheall在所有其他任务完成后返回一个任务,因此您可以等待,您也可以有一个计时器,因此可能在更改按键后有0.5到1秒的延迟,然后再对其执行任何操作,然后,如果用户连续快速键入10个字母,则事件只针对最后一个字母触发,而不是10次。看起来作者真的不关心顺序执行,只关心得到最后一个结果并退出所有其他结果。因此,并行执行对作者是有利的。如果我是对的,那么切换到完全顺序的任务执行在这里看起来不是一个好的解决方案。@Serg我不确定。如果作者确认,我将更改我的答案。不,我不关心顺序执行,因为我不知道用户何时会键入内容。@csharpskolan如果在按下新键时可以取消正在进行的计算,这不是问题;这是实现所需执行顺序的经典方法。您是否可以先传递取消令牌以取消任何机上计算,然后运行当前计算,这是最新的?不,我不确定是否可以取消基础Pri
如果等待取消对用户体验有太大影响,则为NCIPlassearch。也许我会试一试。看来作者真的不在乎顺序执行,而只在乎得到最后一个结果并放弃所有其他结果。因此,并行执行对作者是有利的。如果我是对的,那么切换到完全顺序的任务执行在这里看起来不是一个好的解决方案。@Serg我不确定。如果作者确认,我将更改我的答案。不,我不关心顺序执行,因为我不知道用户何时会键入内容。@csharpskolan如果在按下新键时可以取消正在进行的计算,这不是问题;这是实现所需执行顺序的经典方法。您是否可以先传递取消令牌以取消任何机上计算,然后运行当前计算,这是最新的?不,我不确定是否可以取消基础PrincipalSearcher,或者等待取消是否对用户体验有太大影响。也许我会试一试。你也可以称之为票,而不是上下文。或售票处获取更多样式点:-你也可以称之为票证,而不是上下文。或售票处获取更多样式点:-