Метод асинхронной десериализации C # не отменяет

Я столкнулся с проблемой, когда задача, выполняемая ниже, выполняется асинхронно, но не может быть отменена, когда сериализатор считывает поток памяти. Когда пользователь делает запрос на отмену (нажав кнопку отмены), выполняется отмена (метод cancel () вызывается из токена), но задача продолжается.

Класс обслуживания:
Асинхронный метод, вызываемый из LoadHelper () в основном классе

   public async void StartTask(Action callback, CancellationToken token)
    {
        await Task.Run(callback, token);
    }

Основной класс:

    private void LoadHelper()
    {
        _services.GetInstance<IThreadService>().StartTask(
            () => LoadHelperAsync(), _cancelService.Token);
    }

Метод выполняется асинхронно

    private void LoadHelperAsync()
    {
        var serializer = new DataContractSerializer(typeof(UserDatabase));
        string selectedDir;
        ComparisonResult = null;

        _services.GetInstance<IOpenFileService>().DisplayOpenFileDialog(
            "Select a saved comparison",
            "Comparison File(*.xml; *.cmp)|*.xml;*.cmp",
            Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
            out selectedDir);

        if (!string.IsNullOrEmpty(selectedDir))
        {
            _dispatchService.BeginInvoke(() => IsExecuting = true);
            using (FileStream fileStream = new FileStream(selectedDir, FileMode.Open, FileAccess.Read))
            using (DeflateStream compressedStream = new DeflateStream(fileStream, CompressionMode.Decompress))
            using (BufferedStream regularStream = new BufferedStream(fileStream))
            {
                Stream memoryStream;

                //Use filename to determine compression
                if (selectedDir.EndsWith(".cmp", true, null))
                {
                    memoryStream = compressedStream;
                }
                else
                {
                    memoryStream = regularStream;
                }

                Report("Loading comparison");

                Report(0);
                IsExecuting = true;
                ComparisonResult = (UserDatabase)serializer.ReadObject(memoryStream);

                memoryStream.Close();
                fileStream.Close();
                IsExecuting = false;
                Report("Comparison loaded");
            }
            _dispatchService.BeginInvoke(() => IsExecuting = false);
            _dispatchService.BeginInvoke(() => ViewResults.ExecuteIfAble());
        }
        else
        {
            Report("No comparison loaded");
        }

Код отмены:

Эта команда привязана к кнопке «отменить» в представлении.

     CancelCompare = new Command(o => _cancelService.Cancel(), o => IsExecuting);

Из класса CancellationService

    public class CancellationService : ICancellationService, IDisposable
{
    private CancellationTokenSource _tokenSource;

    public CancellationService()
    {
        Reset();
    }

    public CancellationToken Token { get; private set; }

    public void Cancel()
    {
        _tokenSource.Cancel();
    }

    public void Reset()
    {
        _tokenSource = new CancellationTokenSource();
        Token = _tokenSource.Token;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            _tokenSource.Cancel();
            _tokenSource.Dispose();
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

person Ricky Thomas    schedule 30.07.2018    source источник
comment
Почему у вас async void? Почему вы назвали LoadHelperAsync с суффиксом -Async, если это не async?   -  person mason    schedule 30.07.2018
comment
Добавьте, пожалуйста, как вы отменяете задачу   -  person Camilo Terevinto    schedule 30.07.2018
comment
Аннулирование @RickyThomas является совместным. Вызов .Cancel() не прерывает поток, он сигнализирует токену. Ваше действие должно получить токен и проверить его поднятие. Если вы хотите отменить десериализацию, вы должны использовать асинхронные методы внутри LoadHelperAsync и также передать им токен. Не BeginInvoke, это, по сути, Thread.Start или прямой вызов другого потока, в зависимости от того, что _dispatchService   -  person Panagiotis Kanavos    schedule 30.07.2018
comment
@mason код запускается асинхронно из класса обслуживания. Я думаю, что первоначальный автор сделал это из-за этого, но, к сожалению, я не могу говорить о его выборе в суффиксном соглашении.   -  person Ricky Thomas    schedule 30.07.2018
comment
Возможный дубликат Как отменить задачу в ожидании?   -  person Liam    schedule 30.07.2018
comment
@PanagiotisKanavos, как мне проверить, был ли изменен токен, когда сериализатор читает поток памяти?   -  person Ricky Thomas    schedule 30.07.2018
comment
@RickyThomas прочитал статью Стивена Туба, в которой объясняется как отмена, так и прогресс. Извлечь весь код пользовательского интерфейса из класса LoadHelperAsync. Вам не нужно BeginInvoke, когда вы используете await. Единственное, что должно быть асинхронным, - это вызов serializer.ReadObject и его поток.   -  person Panagiotis Kanavos    schedule 30.07.2018
comment
Жетон отмены - это волшебство. Это просто значение, которое проверяется в основном каждый раз, когда вы вызываете системный асинхронный метод, но если вы не используете какие-либо системные методы, вам нужно проверить его самостоятельно и отреагировать так, как вы сочтете подходящим (иногда это вызывает исключение, иногда он корректно останавливает запущенную задачу, иногда возвращается рано.   -  person cwharris    schedule 30.07.2018


Ответы (1)


Вызов _tokenSource.Cancel (); ничего не сделает с запущенной задачей. Токен отмены прерывает выполнение задачи, только если она была отменена до < / strong> Task.Run

Взглянем:

static void Main(string[] args)
{
    using (var tokenSource = new CancellationTokenSource())
    {
        var aTask = StartTask(() =>
        {
            while (true)
            {
                Console.WriteLine("Nothing is going to happen.");
                // Some long operation
                Thread.Sleep(1000);
            }
        }, tokenSource.Token);

        tokenSource.Cancel();
        aTask.Wait();
    }

    Console.ReadKey();
}

static async Task StartTask(Action callback, CancellationToken cancellationToken)
{
    await Task.Run(callback, cancellationToken);
}

Если вы хотите отменить выполнение, вы должны проверить токен отмены самостоятельно, нет никакой магии, которая отменяет задачу за вас:

static void Main(string[] args)
{
    using (var tokenSource = new CancellationTokenSource())
    {
        var aTask = StartTask(cancellationToken =>
        {
            while (true)
            {
                cancellationToken.ThrowIfCancellationRequested();
                Console.WriteLine("Will stop before that if canceled.");
                // Some long operation
                // Also pass the token all way down
                Task.Delay(1000, cancellationToken).Wait();
            }
        }, tokenSource.Token);

        // Try 0, 500, 1500 to see the difference
        Thread.Sleep(1500);
        tokenSource.Cancel();

        try
        {
            aTask.Wait();
        }
        catch (Exception ex)
        {
            // AggregateException with OperationCanceledException
            Console.WriteLine("Task was canceled.");
            Console.WriteLine(ex.ToString());
        }
    }

    Console.ReadKey();
}

static async Task StartTask(Action<CancellationToken> callback, CancellationToken cancellationToken)
{
    await Task.Run(() => callback(cancellationToken), cancellationToken);
}

Обратите внимание, что этот код только иллюстрирует, как работает задача, никогда не пишите ничего подобного в рабочей среде.

person Kosta_Arnorsky    schedule 30.07.2018