Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Only need 'most recent' Task - best practices for cancelling/ignoring?

I have a task that looks like this:

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

LongMethod calls a long running service, during which I can’t (or at least, don’t think I can), constantly poll a cancellation token to see if it has been cancelled. However, I am interested in ‘cancelling’, or ignoring, the callback method.

When the TaskCallback is called, I am only interested in the ‘result’ if it is the from the most recent task (let us assume that the service that LongMethod calls preserves order, and also assume that the user can click the button numerous times, but only the latest one is relevant).

I have modified my code in the following way: after a task is created, I add it to the top of a stack. In the TaskCallback, I check to see if the task that has been passed to the callback is the most recent one (i.e. a TryPeek at the top of the stack). If it is not, I just ignore the result.

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
}

I am quite certain that this isn’t a ‘best practices’ solution. But what is? Passing and maintaing cancellation tokens doesn’t really seem all that elegant, either.

like image 694
user981225 Avatar asked Jun 22 '12 20:06

user981225


People also ask

What is the way to create a task that can be Cancelled after it starts?

Create and start a cancelable task. Pass a cancellation token to your user delegate and optionally to the task instance. Notice and respond to the cancellation request in your user delegate. Optionally notice on the calling thread that the task was canceled.

Which of the following option should be used to implement cancellation for a long running task?

In this article, we will learn How to cancel or interrupt the Long Running Task using a Cancellationtokensource method in . NET 4.0. In this article, we are going to learn how to cancel or interrupt the Long Running Task using the Cancellationtokensource method in .

Does CancellationToken throw exception?

An exception thrown by this operation represents cancellation only when its type inherits from OperationCanceledException and when the CancellationToken. IsCancellationRequested property is true .


2 Answers

I personally find the following approach to be the most elegant:

// 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
}
like image 162
Douglas Avatar answered Sep 22 '22 07:09

Douglas


I understand that you cannot cancel your long running task but want the process to be instantly aborted from the users point of view when he cancels.

Start a cancel task in parallel with your long running task. Continue off of Task.WhenAny(longRunningTask, cancelTask) and check, if the completed task was the long running task or the cancel task. You can then decide what to do (show results or update UI).

When you cancel the "cancel task" the continuation will instantly fire.

like image 26
usr Avatar answered Sep 22 '22 07:09

usr