0

In my view I have two buttons, one calling the Start() action and one calling Stop(). Here is what the controller looks like:

public class TestController : AsyncController
{
    public static CancellationTokenSource cts = new CancellationTokenSource();

    public void StartAsync()
    {
        cts = new CancellationTokenSource();
        CancellationToken cancellationToken = cts.Token;
        AsyncManager.OutstandingOperations.Increment();

        AsyncManager.Parameters["task"] = Task.Factory.StartNew(() =>
        {
            try
            {
                while() {
                    if (cancellationToken.IsCancellationRequested)
                        break;

                    // Do stuff
                }
            }
            finally
            {
                AsyncManager.OutstandingOperations.Decrement();
            }
        }, cancellationToken);
    }

    public ActionResult StartCompleted(Task task)
    {
        try
        {
            task.Wait();
            return Json(new { success = true }, JsonRequestBehavior.AllowGet);
        }
        catch (Exception e)
        {
            return Json(new { success = false }, JsonRequestBehavior.AllowGet);
        }
    }

    public void StopAsync()
    {
        cts.Cancel();
    }

    public ActionResult StopCompleted()
    {
        return Json(new { success = true }, JsonRequestBehavior.AllowGet);
    }
}

But when I click the stop button, the Stop action is called only after Start finishes. How am I supposed to stop the action?

Lucas Mello
  • 43
  • 1
  • 5

2 Answers2

2

Quote from the book Pro .NET Performance:

Cancellation of already executing work requires cooperation from the code executing that work. However, tasks that have not begun executing yet can be cancelled completely without any malignant consequences.

This means that in the Task lambda itself you need to check if the work has been cancelled or not and act accordingly. For example:

if (cts.IsCancellationRequested) return;

Are you checking for cancellation requests?

Update

The check needs to be somewhere in the // Do stuff part. So for example if you are running a complex multipart calculation as a task where you have to loop through hundreds of items, do some processing and give back some result at the end, you can check for cancellation at the start of each iteration of the loop.

Or another example if inside your task you’re using some API which supports cancellation (like HttpClient.GetAsync) then you have to pass your CancellationToken to that API’s appropriate overload.

Szabolcs Dézsi
  • 8,743
  • 21
  • 29
  • It would be worth explicitly stating that this `if` statement would need to be in the code block with the `// Do stuff` comment. – Enigmativity Sep 28 '17 at 04:31
  • I am checking for cancellation requests, updated the code to reflect that. But the main problem here is that the Stop is never called. – Lucas Mello Sep 28 '17 at 13:14
0

I needed to use

[SessionState(SessionStateBehavior.ReadOnly)]

to allow my controller to handle requests asynchronously. Since I'm using MVC 2 and that attribute is not available, I had to create a custom route handler. Serdar's answer on this question helped me with that: Disable Session state per-request in ASP.Net MVC

Lucas Mello
  • 43
  • 1
  • 5