首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >任务不取消CancellationTokenSource

任务不取消CancellationTokenSource
EN

Stack Overflow用户
提问于 2022-03-18 15:09:56
回答 2查看 600关注 0票数 0

我有个密码。这样做的目的是取消一个带有CancellationToken,的任务,我知道它可以用返回; in循环,但是我想用CancellationToken.来完成它。我试着去做,但它不起作用,我也不知道为什么。

任务中断dropNumber上的任务循环

代码语言:javascript
复制
static CancellationTokenSource cancellationTokenSource = null;

    static async Task Main(string[] args)
    {
        cancellationTokenSource = new CancellationTokenSource();

        try
        {
            Task.Run(() => CountLoopAsync(cancellationTokenSource.Token, 4),cancellationTokenSource.Token);
        }
        catch(OperationCanceledException ex)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.BackgroundColor = ConsoleColor.White;
            Console.WriteLine("Task is cancelled!");
            Console.ResetColor();
        }
        finally
        {
            cancellationTokenSource.Dispose();
        }
    }

    private static void CountLoopAsync(CancellationToken token, int dropNumber)
    {
            for(int i = 0; i < 10; i++)
            {
                Console.WriteLine(i);
                if (dropNumber == i)
                {
                    cancellationTokenSource.Cancel();
                }

            }

    }

    }
EN

回答 2

Stack Overflow用户

发布于 2022-03-18 15:14:31

你的Task.Run,它已经完成了等待,所以你不会去取消句子,直到任务完成。使用Task.Run而不等待允许继续运行并执行取消

更新

我认为您的示例如果不是很好,因为您试图以顺序方式执行所有代码,而任务的使用通常是在后台以异步形式运行代码。此外,我认为使用预定义值停止是一种非意义的做法:在这种情况下,更改"for“中的最后一步,而不是使用令牌。你不能在任务代码中取消。如果在该代码中知道何时取消,则只需返回。令牌的用途是允许外部取消任务代码。如果这样做,您就无法控制任务何时完成,因为它取决于外部的一些东西。例如,当用户单击“取消”按钮时,可能会出现这种情况。通常,您的计数器代码试图计算所有。但是,经过很长一段时间的操作,您随时都会给用户以取消的机会。

将代码封装在一个类中:

代码语言:javascript
复制
public class YourClass : IDisposable
{
    private CancellationTokenSource _cancellationTokenSource = null;

    private Task _task = null;

    public void Wait(int milliSeconds)
    {
        this._task.Wait(milliSeconds, this._cancellationTokenSource.Token);
    }

    public void Dispose()
    {
        this._cancellationTokenSource?.Dispose();
        this._task?.Dispose();
    }

    public async Task RunLongOperationInBackground()
    {
        this._cancellationTokenSource = new CancellationTokenSource();

        this._task = Task.Run(
            () => CountLoopAsync(this._cancellationTokenSource.Token),
            this._cancellationTokenSource.Token);

        await this._task;
    }

    public void Abort()
    {
        // Cancel the operation
        this._cancellationTokenSource?.Cancel();
    }

    private static void CountLoopAsync(CancellationToken token)
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine(i);

            // Uncomment to simulate task takes some time to finish
            //Thread.Sleep(3000);

            // You don't know when the action will be cancelled. If you know that, you don't 
            // need the cancellation: you can do the for until your well known end
            if (token.IsCancellationRequested)
            {
                break;
            }
        }
    }
}

这个类允许您运行一个操作(RunLongOperationInBackground),也可以在任何时候取消操作(中止)。因此,您运行您的任务,并在任何时候,您可以取消任务。如果查看CountLoopAsync代码,它尝试执行所有内容,但它有时会检查令牌(在本例中的每次迭代中),如果有人请求取消,则退出for。但你可以随心所欲。例如,您可能一直运行到下一个100,因此,即使令牌已被取消,您也可以继续运行到下一个100。或者,如果在操作接近结束时要求取消,您可以决定继续。代币只告诉你外面要终止。

使用2个按钮创建一个表单(而不是一个控制台),这是一个更现实的例子:

代码语言:javascript
复制
public partial class Form1 : Form
{
    private readonly YourClass _yourClass;

    public Form1()
    {
        this.InitializeComponent();

        this._yourClass = new YourClass();
    }

    private async void OnStartButtonClick(object sender, EventArgs e)
    {
        await this._yourClass.RunLongOperationInBackground();
    }

    private void OnCancelButtonClick(object sender, EventArgs e)
    {
        this._yourClass.Abort();
    }

    private void OnForm_FormClosed(object sender, FormClosedEventArgs e)
    {
        if (this._yourClass != null)
        {
            // Wait, for example 30 seconds before end the appication
            this._yourClass.Wait(30000);

            this._yourClass.Dispose();
        }
    }
}

在构造函数中创建类。“开始”按钮运行您的长时间操作(您可能希望在每次迭代中使用延迟,以便能够在终止之前取消)。在任何时候,您都可以单击“取消操作”按钮来取消该操作。在那个时刻,在您的" for“中,令牌告诉您已被取消,然后退出for。

票数 0
EN

Stack Overflow用户

发布于 2022-03-18 18:46:44

我认为问题在于:

代码语言:javascript
复制
cancellationTokenSource.Dispose();

cancellationTokenSource似乎被过早地处理了。您不应该在所有相关工作完成之前将其释放。在您的情况下,您可能必须等待Task.Run的完成才能调用Dispose

代码语言:javascript
复制
Task.Run(() => CountLoopAsync(cancellationTokenSource.Token, 4),
    cancellationTokenSource.Token).Wait();
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71529142

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档