CancellationToken является даже нулевым - PullRequest
0 голосов
/ 30 октября 2018

В моем приложении я запускаю Task, который выполняет тяжелую операцию, я пытаюсь остановить выполнение этого Task. На самом деле я объявил в своем классе следующую структуру:

public class Foo
{
    private CancellationTokenSource tokenSource = new CancellationTokenSource();
    private CancellationToken token;

    public void Start()
    {
        var task = new Task(async () => {
           try
           {
               await new Bot().StartAsync(token);
           }
           catch(Exception ex)
           {
              Console.WriteLine(ex.ToString());
           }
        }, token));

        task.Start();
    }
}

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

StopTask_Click(object sender, EventArgs e) 
{
    tokenSource.Cancel();
}

Теперь внутри метода StartAsync у меня есть следующий код:

public async Task StartAsync(CancellationToken token)
{
    ApplicationToken = token;

    while(true)
    {
       if(ApplicationToken.IsCancellationRequested)
       {
           break;
       }
    }
}

В ApplicationToken хранится токен, переданный в качестве параметра в классе метода StartAsync.

После события нажатия кнопки запрос должен быть отменен, но ничего не происходит.

Затем я проверяю для каждой итерации, был ли сделан запрос на отмену, но значение переменной даже false.

Ответы [ 4 ]

0 голосов
/ 30 октября 2018

Во-первых, StartAsync не хватает операторов ожидания, и он будет работать синхронно.

public async Task StartAsync(CancellationToken token)
{
    ApplicationToken = token;

    while (true)
    { 
        if (ApplicationToken.IsCancellationRequested) break;
        await Task.Delay(10, ApplicationToken); // => you may change it to concrete task
    }
}

Во-вторых, класс CancellationToken в Foo является избыточным, вы даже не инициализируете его.

new Bot().StartAsync(tokenSource.Token); внутри метода Start не ожидается, поэтому выполнение текущего метода продолжается до завершения вызова.

И поскольку метод Start является недействительным, вы должны использовать метод GetAwaiter(), чтобы выполнить задачу в классе Bot.

public void Start()
{
    try
    {
        new Bot().StartAsync(tokenSource.Token).GetAwaiter();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

Наконец, если метод StopTask_Click является членом класса Foo, он должен нормально работать.


Это пример того, как отменить задачу, используя CancellationTokenSource

public partial class Form1 : Form
{
    private readonly Foo foo = new Foo();

    private void button1_Click(object sender, EventArgs e)
    {
        foo.Start();          
    }

    private void button2_Click(object sender, EventArgs e)
    {
        foo.Cancel();
    }
}

public class Foo
{
    private readonly CancellationTokenSource tokenSource = new CancellationTokenSource();

    public void Start()
    {
        try
        {
            new Bot().StartAsync(tokenSource.Token).GetAwaiter();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

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

public class Bot
{
    private CancellationToken ApplicationToken;

    public async Task StartAsync(CancellationToken token)
    {
        ApplicationToken = token;

        while (true)
        { 
            if (ApplicationToken.IsCancellationRequested) break;
            await Task.Delay(10, ApplicationToken);
        }
    }
}
0 голосов
/ 30 октября 2018

Вы передаете токен, отличный от того, который вы пытаетесь отменить. Вы можете устранить

private CancellationToken token;

и вместо этого перейдите к StartAsync()

tokenSource.Token
0 голосов
/ 30 октября 2018

Не могли бы вы попробовать ниже:

public class Foo
{
   var cancellationTokenSource = new CancellationTokenSource();
   var token = cancellationTokenSource.Token();

   public void Start() {
        var t = Task.Factory.StartNew(() =>
        {
            try
            {
               await new Bot().StartAsync(token);
            }
            catch(Exception ex)
            {
               Console.WriteLine(ex.ToString());
            }
        }, token).ContinueWith(task =>
          {
              if (!task.IsCompleted || task.IsFaulted)
              {
                 // Log
              }
          }, token);
    }

    StopTask_Click(object sender, EventArgs e) 
    {
          cancellationTokenSource.Cancel();
    }
}
0 голосов
/ 30 октября 2018

Ну, похоже, ты забыл token = tokenSource.Token;

Edit1: вы должны проверить отмену, используя ThrowIfCancellationRequested()

Edit2:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace CancellationTokenPOC
{
class Program
{
    public static async Task Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        TokenPOC t = new TokenPOC();
        var longRunningTask = Task.Factory.StartNew(async () =>
        {
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(i);
                t.token.ThrowIfCancellationRequested();

                await Task.Delay(10000);
            }

        });
        Console.ReadKey();
        t.source.Cancel();
        await Task.Delay(1000);
        Console.WriteLine("finishing");
    }
}

class TokenPOC
{
    public CancellationTokenSource source = new CancellationTokenSource();
    public CancellationToken token;
    public TokenPOC()
    {
        token = source.Token;
    }
}
}

Этот токен отменяется и завершает программу, как и ожидалось ...

...