5

我有一个看起来像这样的任务:

var task = Task.Factory.StartNew <object>(LongMethod);
task.ContinueWith(TaskCallback, TaskScheduler.FromCurrentSynchronizationContext());

LongMethod 调用一个长时间运行的服务,在此期间我不能(或者至少,我认为我不能)不断地轮询取消令牌以查看它是否已被取消。但是,我对“取消”或忽略回调方法感兴趣。

当 TaskCallback 被调用时,我只对来自最近任务的“结果”感兴趣(让我们假设 LongMethod 调用的服务保留了顺序,并假设用户可以多次单击按钮,但只有最新的一个是相关的)。

我通过以下方式修改了我的代码:创建任务后,我将其添加到堆栈的顶部。在TaskCallback 中,我检查已传递给回调的任务是否是最新的(即堆栈顶部的TryPeek)。如果不是,我只是忽略结果。

private ConcurrentStack<Task> _stack = new ConcurrentStack<Task>();

private void OnClick(object sender, ItemClickEventArgs e)
{
    var task = Task.Factory.StartNew < object >( LongMethod);
    task.ContinueWith(TaskCallback, TaskScheduler.FromCurrentSynchronizationContext());
    _stack.Push(task);
 }


private void TaskCallback(Task<object> task)
{
    Task topOfStack;
    if(_stack.TryPeek(out topOfStack)) //not the most recent
    {
        if (task != topOfStack) return;
    }
    //else update UI
}

我很确定这不是“最佳实践”解决方案。但究竟是什么?传递和维护取消令牌似乎也不是那么优雅。

4

2 回答 2

6

我个人认为以下方法是最优雅的:

// Cancellation token for the latest task.
private CancellationTokenSource cancellationTokenSource;

private void OnClick(object sender, ItemClickEventArgs e)
{
    // If a cancellation token already exists (for a previous task),
    // cancel it.
    if (this.cancellationTokenSource != null)
        this.cancellationTokenSource.Cancel();

    // Create a new cancellation token for the new task.
    this.cancellationTokenSource = new CancellationTokenSource();
    CancellationToken cancellationToken = this.cancellationTokenSource.Token;

    // Start the new task.
    var task = Task.Factory.StartNew<object>(LongMethod, cancellationToken);

    // Set the task continuation to execute on UI thread,
    // but only if the associated cancellation token
    // has not been cancelled.
    task.ContinueWith(TaskCallback, 
        cancellationToken, 
        TaskContinuationOptions.NotOnCanceled, 
        TaskScheduler.FromCurrentSynchronizationContext());
}

private void TaskCallback(Task<object> task)
{
    // Just update UI
}
于 2012-06-22T21:31:38.950 回答
1

我了解您无法取消长时间运行的任务,但希望在用户取消时立即从用户的角度中止该过程。

与长时间运行的任务并行启动取消任务。继续Task.WhenAny(longRunningTask, cancelTask)检查,完成的任务是长时间运行的任务还是取消任务。然后您可以决定做什么(显示结果或更新 UI)。

当您取消“取消任务”时,延续将立即触发。

于 2012-06-22T21:44:02.943 回答