【问题标题】:How to run Tasks such that the last one started (not completed) wins?如何运行任务以使最后一个开始(未完成)获胜?
【发布时间】:2020-09-16 10:30:22
【问题描述】:

我正在尝试实现一个响应式搜索文本框,其中搜索结果会随着用户输入搜索文本而更新。我正在尝试使用 Tasks 执行此操作,但我发现有时之前的搜索会在稍后的搜索之后完成,从而将新结果替换为旧结果。

一种快速解决方法是在用户停止输入半秒左右后延迟开始搜索。但是,我认为这只会隐藏问题。我想找到一个通用的解决方案。

到目前为止,我已经提出了以下课程:

public class LastAddedTaskStrategy<T>
{
    private DateTime latest;

    public T Result { get; private set; }

    public async Task Add(Task<T> t)
    {
        var timestamp = DateTime.Now;
        latest = timestamp;

        var currentResult = await t;

        if (timestamp >= latest)
            Result = currentResult;
    }
}

这个想法是,您可以添加(和等待)任意数量的任务(例如,搜索),但只有最后添加的(不是最后完成的)任务的结果才能获胜。因此,如果之前的搜索在之后的搜索之后完成,Results 属性将不会更新,因为它是旧结果。

这是一个好的解决方案还是有更好的解决方案?

【问题讨论】:

  • 那么当添加一个新任务时,您是否可以取消任何正在运行的任务,因为您只是要丢弃它们的结果?
  • 我认为你应该取消旧任务。
  • 而不是DateTime.Now,您可以对Interlocked.Incremented int / long 执行相同的操作 - 以避免DateTime.Now 可能对多个@987654329 具有相同值的问题@s.
  • @RufusL 我想过这个问题,但我不确定该怎么做。根据 C# Cookbook 中的 Concurrency,即使 Task 被取消,它实际上也有可能成功完成(例如,在请求取消时它可能几乎完成)。
  • @redcurry 当您取消任务时,您可以检查 Task.Status 是否已取消。以这种方式可以忽略取消的任务。查看system.threading.tasks.taskstatus 了解更多信息。

标签: c# asynchronous concurrency async-await task


【解决方案1】:

这是System.Windows.Forms 控件的扩展方法,它订阅它们的TextChanged 事件。每次触发事件时都会调用一个异步方法,并将此方法的结果传播到处理程序。只有在异步方法完成前不会被抢占的情况下才会传播结果。

public static void OnTextChangedExecute<TResult>(this Control control,
    Func<CancellationToken, Task<TResult>> function,
    Action<TResult> handler)
{
    CancellationTokenSource activeCTS = null;
    control.TextChanged += Event_Handler;

    async void Event_Handler(object sender, EventArgs args)
    {
        activeCTS?.Cancel();
        TResult result;
        using (var cts = new CancellationTokenSource())
        {
            activeCTS = cts;
            try
            {
                result = await function(cts.Token);
                cts.Token.ThrowIfCancellationRequested();
            }
            catch (OperationCanceledException)
                when (cts.Token.IsCancellationRequested)
            {
                return; // Preempted, don't invoke the handler.
            }
            finally
            {
                if (activeCTS == cts) activeCTS = null;
            }
        }
        handler(result);
    }
}

使用示例:

public Form1()
{
    InitializeComponent();
    TextBox1.OnTextChangedExecute(
        ct => SearchAsync(TextBox1.Text, ct), TextBox1_SearchCompleted);
}

async Task<int> SearchAsync(string text, CancellationToken token)
{
    await Task.Delay(1000, token); // Simulate some cancelable I/O operation
    return Int32.Parse(text);
}

void TextBox1_SearchCompleted(int result)
{
    MessageBox.Show($"Result: {result}");
}

OnTextChangedExecute 方法不是线程安全的。它只能从 UI 线程调用。这取决于存在 WindowsFormsSynchronizationContext 才能正常工作。

【讨论】:

    猜你喜欢
    • 2021-05-02
    • 2021-06-14
    • 1970-01-01
    • 1970-01-01
    • 2015-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多