【问题标题】:Fire multiple async tasks and only update result from latest task触发多个异步任务,仅更新最新任务的结果
【发布时间】:2021-01-29 11:57:19
【问题描述】:

我一直在修补 Blazor,但遇到了异步/等待问题。我添加了一个搜索输入字段,该字段触发异步操作,当用户开始输入时,该操作会向用户返回搜索结果。问题是异步任务以错误的顺序完成。触发的第一个任务返回并使已完成的后续任务的结果无效。

我提出了一个涉及跟踪任务的解决方案,但这是正确的做法还是我做错了?

我目前的解决方案是这样的:

        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();
            }
        }

【问题讨论】:

  • 你怎么知道一个任务是“最后一个”?用户键入时不会添加更多任务吗?
  • 我相信您的方法是正确的——您确实需要跟踪任务并检查刚刚完成的任务是否是最新的。我只能建议只存储最后运行的任务而不是整个列表。您也可以考虑在开始新任务之前取消之前的任务。
  • 您真正想要实现什么?总是最后触发的查询结果很重要 - 所以可以删除较早的查询,或者您需要所有触发请求的结果?
  • 我相信Task.WhenAll 可以帮助你。 Task.WhenAll 在所有其他任务完成后返回一个任务,因此您可以等待
  • 你也可以有一个计时器,所以在改变(击键)之后会有一点延迟(可能是 0.5 到 1 秒),然后再做任何事情,然后如果用户输入 10 个字母快速连续事件仅触发最后一个字母,而不是 10 次。

标签: c# async-await blazor


【解决方案1】:

对于这种场景,我使用我称之为“异步上下文”的东西(可能是一个非常糟糕的名字,因为“上下文”现在可能意味着很多东西)。您只需要某种唯一值(new object() 就足够了),将此值捕获到await 之前的局部变量中,然后在await 之后进行比较。如果它们不匹配,则该代码知道它不再是当前代码。

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;
  }
}

【讨论】:

  • 您也可以将其称为ticket,而不是context。或ticketToRide 获取更多风格点。 :-)
【解决方案2】:

一般来说,有顺序和并行组合(用于异步操作)。如果您希望事情以某种特定的顺序发生(确实是您想要的),您就不能并行运行。请看下面的代码:

var enumerableOfTasks = ... // whatever enumerable of tasks in some desired order
foreach (var task in enumerableOfTasks) await task;

...所有的魔法都发生在await task 点。除非当前任务完成,否则不会执行下一个任务。这保证您最后执行的任务不会因竞争条件而被某些前任覆盖。

【讨论】:

  • 看来作者真的不关心顺序执行,只关心得到最后一个结果,然后退出所有其他的。所以,并行执行对作者有好处。如果我是对的,那么切换到完全顺序的任务执行在这里看起来不是一个好的解决方案。
  • @Serg 我不确定。如果作者确认,我会改变我的答案。
  • 不,我不关心顺序执行,因为我不知道用户何时会输入内容。
  • @csharpskolan 如果您可以在按下新键时取消正在进行的计算,这不是问题;这是实现所需执行顺序的经典方式。您可以传递取消令牌以先取消任何飞行中的计算,然后运行当前的,即最新的吗?
  • 不,我不确定是否可以取消底层的 PrincipalSearcher,或者等待取消对用户体验的影响是否太大。也许我会试一试。
猜你喜欢
  • 1970-01-01
  • 2018-02-16
  • 1970-01-01
  • 2017-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多