Отменить задачу внутри маршрута

Я создаю приложение С#, используя Nancy API. У меня есть асинхронная операция, которая запускает очень длинный алгоритм оптимизации, который пользователь должен иногда отменять. Псевдокод выглядит следующим образом:

        Get["/algorithm/run/", true] = async (parameters, ct) =>
        {
                var algy = new Algorithm();
                var result = await Task.Run(() => algy.RunAlgorithm(ct), ct);
        };

Как мне отменить CancellationToken (ct) или создать новый метод отмены алгоритма?

Альтернатива, которую я пробовал, выглядит примерно так:

var cts = new CancellationTokenSource();     
var cToken = cts.Token;

    Get["/algorithm/run/", true] = async (parameters, ct) =>
    {
      var algy = new Algorithm();
      var result = await Task.Run(() => algy.RunAlgorithm(cToken), cToken);
    };


    Get["/schedule/stop"] = _ =>
    {
        cts.Cancel();
    };

Но это, очевидно, не работает, так как маршрут находится в своей собственной асинхронной задаче.

Я прочитал статью, размещенную здесь: http://blog.jonathanchannon.com/2013/08/24/async-route-handling-with-nancy/, в котором упоминается:

CancellationToken передается, поэтому вы можете проверить свойство ct.IsCancellationRequested, чтобы определить, хотите ли вы совместно отменить обработку в обработчике маршрута. Это свойство может быть установлено, например, в случае внутренней ошибки или промежуточного программного обеспечения, которое решает отменить запрос, или хост отключается. Если вы не знали, что Нэнси совместима с OWIN и совместима почти с тех пор, как вышла спецификация OWIN.

Любая помощь будет высоко оценена, поскольку я новичок в обработке потоков.

Полный пример:

using Nancy;
using Nancy.Hosting.Self;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace NancyApp
{
class Program
{
    private static NancyHost NancyHost { get; set; }
    private static HttpClient Client { get; set; }

    static void Main(string[] args)
    {
        var configuration = new HostConfiguration()
        {
            UrlReservations = new UrlReservations() { CreateAutomatically = true }
        };

        NancyHost = new NancyHost(configuration, new Uri("http://localhost:1234"));

        NancyHost.Start();

        Client = new HttpClient();
        Client.Timeout = new TimeSpan(1, 0, 0);
        Client.BaseAddress = new Uri("http://localhost:1234");

        Console.WriteLine("Hosting...\n");

        Client.GetAsync("http://localhost:1234/run");
        System.Threading.Thread.Sleep(5000);
        Client.GetAsync("http://localhost:1234/cancel");
        Console.ReadLine();
    }
}

public class TestModule : NancyModule
{
    CancellationTokenSource cts = new CancellationTokenSource();

    public TestModule()
    {
        Get["/run", true] = async (parameters, ct) =>
        {
            Algorithm ag = new Algorithm();
            Console.Write("Running Algorithm...\n");
            var result = await Task.Run(() => ag.RunAlgorithm(cts.Token), cts.Token);

            return Response.AsText(result.ToString());
        };

        Get["/cancel"] = _ =>
        {
            Console.Write("Cancel requested recieved\n");
            cts.Cancel();
            return Response.AsText("Foo");
        };

    }
}

class Algorithm
{
    public Algorithm()
    {

    }

    public int RunAlgorithm(CancellationToken cToken)
    {
        int min = Int32.MinValue;

        while (min < Int32.MaxValue)
        {
            if (!cToken.IsCancellationRequested)
            {
                min++;
            }
            else
            {
                Console.Write("IsCancellationRequested true!\n");
                cToken.ThrowIfCancellationRequested();
            }
        }
        return min;
    }
}
}

person Phrank    schedule 15.08.2016    source источник
comment
Я не знаю Нэнси, но похоже, что у него есть собственный токен, который он передает вам. Тем не менее, вам не нужно ограничиваться проверкой только одного токена. Вы можете использовать тот, который он передает и тот, который вы создаете, отменяя, если установлен любой из. Если вам нужно больше деталей, вам, вероятно, следует улучшить вопрос, чтобы он включал хороший минимально воспроизводимый пример, чтобы было ясно, что конкретно у вас проблемы с. В приведенном выше примере кода я не вижу ничего особенно очевидного в качестве препятствия. Обратите внимание, что вы можете использовать CreateLinkedTokenSource(), чтобы упростить использование двух токенов одновременно.   -  person Peter Duniho    schedule 15.08.2016
comment
Чтобы быть более ясным во втором примере: хотя я думал, что это сработает, когда я вызываю cts.Cancel(), объект cToken никогда не возвращает true, когда я вызываю cToken.IsCancellationRequested внутри моего algy.RunAlgorithm(cToken).   -  person Phrank    schedule 15.08.2016
comment
когда я вызываю cts.Cancel(), объект cToken никогда не возвращает true, когда я вызываю cToken.IsCancellatio‌​nRequested внутри моего algy.RunAlgorithm(cT‌​oken) — это неправдоподобно, и, конечно, если вы считаю, что это то, что происходит, - это утверждение, которое действительно должно поддерживаться хорошим минимально воспроизводимым примером, который надежно воспроизводит это поведение. Гораздо более вероятно, что вы только думаете, что отменили тот же токен, который вы передали RunAlgorithm(), и вместо этого имеете дело с двумя разными. Но без хорошего MCVE невозможно предложить, как решить проблему.   -  person Peter Duniho    schedule 15.08.2016
comment
Я включил редактирование с минимальным примером. Спасибо за ваши усилия @PeterDuniho и за то, что указали мне на эту статью.   -  person Phrank    schedule 15.08.2016
comment
Опять же, я недостаточно знаком с Нэнси, чтобы авторитетно судить о коде. Но выделяются две вещи: во-первых, на моем ПК я могу выполнить итерацию от int.MinValue до int.MaxValue менее чем за 5 секунд до вызова операции отмены. Во-вторых, вы уверены, что Нэнси создает только один экземпляр TestModule? Что произойдет, если вы сделаете cts полем static?   -  person Peter Duniho    schedule 15.08.2016
comment
@PeterDuniho Это помогло! Я сделал CancellationTokenSource статическим, и он работает! Думаю, я не понял, что структура Нэнси создала более одного модуля... Мне придется перечитать часть их документации. Спасибо за вашу помощь!   -  person Phrank    schedule 15.08.2016
comment
Рад помочь. Вы должны опубликовать свое решение в качестве ответа и принять его. Это поможет другим, у кого есть похожая проблема, найти ваш вопрос, а также покажет, что вы действительно решили проблему, чтобы люди, которые ищут вопросы без ответов, не отвлекались на этот вопрос.   -  person Peter Duniho    schedule 15.08.2016
comment
Со статическим CancellationTokenSource вы могли запускать неограниченное количество задач, пока не вызовете отмену. После того, как вы вызовете отмену, любая задача будет отменена, и любой вызов для повторного запуска также будет отменен. Это ваше намерение?   -  person Sir Rufo    schedule 20.08.2016
comment
Кстати, вы также можете прочитать stackoverflow.com/q/33764366/251311.   -  person zerkms    schedule 27.08.2016
comment
@zerkms Спасибо за внимание! Я не понимал, что это была плохая практика, и вношу соответствующие коррективы!   -  person Phrank    schedule 28.08.2016


Ответы (1)


Рабочие исправления:

public class TestModule : NancyModule
{
    static CancellationTokenSource cts = new CancellationTokenSource();
    static CancellationToken cToken = cts.Token;

    public TestModule()
    {
        Get["/run", true] = async (parameters, ct) =>
        {
            Algorithm ag = new Algorithm();
            Console.Write("Running Algorithm...\n");
            var result = await Task.Run(() => ag.RunAlgorithm(cToken), cToken);

            return Response.AsText(result.ToString());
        };

        Get["/cancel"] = _ =>
        {
            Console.Write("Cancel requested recieved\n");
            cts.Cancel();
            cts.Dispose();
            cts = new CancellationTokenSource();
            cToken = cts.Token;
            return Response.AsText("Cancelled!");
        };
    }
}

Спасибо @Peter Duniho за помощь.

person Phrank    schedule 27.08.2016