CancellationTokenSource.Cancel在WPF中没有按预期工作

时间:2014-03-16 19:47:44

标签: .net wpf task async-await cancellationtokensource

尝试学习任务,等待和取消 从我认为是MSDN的简单样本开始 Task.Wait Method (CancellationToken)

作为控制台应用程序,它按预期运行 相同的代码在WPF中没有按预期运行 在WPF中t2运行完成 - 在调试中打印任务完成的行 它命中0到100000000之间的OperationCanceledException但是t2继续运行 (注意必须将其更改为ctr< Int32.MaxValue或它永远不会完成)

在.NET 4.5上,还尝试了4.51

public void TestCancel1()
{
    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken token = cts.Token;

    Task.Run(() =>
    {
        cts.Cancel();
        if (token.IsCancellationRequested)
            Debug.WriteLine("Cancellation requested in Task {0}.",
                                Task.CurrentId);
    }, token);
    Task t2 = Task.Run(() =>
    {
        Debug.WriteLine(Int32.MaxValue.ToString());
        for (Int32 ctr = 0; ctr < Int32.MaxValue; ctr++)  // 
        {
            if (ctr % 100000000 == 0)
                Debug.WriteLine(ctr.ToString());
        }
        Debug.WriteLine("Task {0} finished.",
                            Task.CurrentId);
    });
    try
    {
        t2.Wait(token);
    }
    catch (OperationCanceledException)
    {
        Debug.WriteLine("OperationCanceledException in Task {0}: The operation was cancelled.", 
                         t2.Id);
    }
}

控制台应用程序上存在问题 这是代码

static void Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();
    CancellationToken token = cts.Token;

    Task.Run(() =>
    {
        Thread.Sleep(1000);  // change number and will get a differnt last ctr
        cts.Cancel();
        if (token.IsCancellationRequested)
            Console.WriteLine("Cancellation requested in Task {0}.",
                                Task.CurrentId);
    }, token);
    Task t2 = Task.Run(() =>
    {
        Console.WriteLine(Int32.MaxValue.ToString());
        for (int ctr = 0; ctr < Int32.MaxValue; ctr++)
        {
            Console.WriteLine(ctr.ToString());
            if (ctr % 100000000 == 0) Console.WriteLine(ctr.ToString());
        }
        Console.WriteLine("Task {0} finished.",
                            Task.CurrentId);
    });
    try
    {
        t2.Wait(token);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("OperationCanceledException in Task {0}: The operation was cancelled.",
                            t2.Id);
    }
}

添加了ThrowIfCancellationRequested
我在该行收到错误。用户代码未处理OperationCancelException

Task t2 = Task.Run(() =>
{
    Debug.WriteLine(Int32.MaxValue.ToString());
    for (Int32 ctr = 0; ctr < Int32.MaxValue; ctr++)  // 
    {                   
        if (ctr % 100000000 == 0)
        {
            Debug.WriteLine(ctr.ToString() + " " + token.IsCancellationRequested.ToString());
        }
        if (token.IsCancellationRequested)
            token.ThrowIfCancellationRequested();
    }
    Debug.WriteLine("Task {0} finished.",
                        Task.CurrentId);
}); //, token);

好的,我知道这已经很久了 来自微软的样本充其量也是误导性的 - 为什么你会有一个取消的样本并没有真正取消 为了验证为什么Rohit说我添加了以下内容 当然,我可以在按下之前看到更多的传球,任何关键都可以继续

    catch (OperationCanceledException)
    {               
        Console.WriteLine("OperationCanceledException in Task {0}: The operation was cancelled.",
                            t2.Id);
        Thread.Sleep(10);
        Console.WriteLine("exit exception");
    }

2 个答案:

答案 0 :(得分:2)

您的 控制台 测试代码可能存在错误并且您的期望错误。在控制台应用程序中,2t继续运行,但程序在t2完成之前退出。

取消令牌来源是“合作取消”,如果您希望t2提前结束,则需要在取消发生时检查t2内部。

Task t2 = Task.Run(() =>
{
    Debug.WriteLine(Int32.MaxValue.ToString());
    for (Int32 ctr = 0; ctr < Int32.MaxValue; ctr++)  // 
    {
        if (ctr % 100000000 == 0)
            Debug.WriteLine(ctr.ToString());
        token.ThrowIfCancellationRequested();
    }
    Debug.WriteLine("Task {0} finished.",
                        Task.CurrentId);
});

答案 1 :(得分:1)

这与WPF无关。控制台应用程序,WPF或WinForms的输出相同。

  

作为控制台应用程序,它按预期运行。同样的代码   没有在WPF中按预期运行。在WPF中t2运行完成 - 该行   完成的任务将在debug中打印。

我怀疑你在使用ctr <= Int32.MaxValue进行测试的控制台应用程序中这就是为什么没有打印Finised的原因,因为它无限运行并且Finish永远不会在控制台上打印出来。


  

它在0到100000000之间击中OperationCanceledException但是t2   继续跑步。

异常不会阻止任务t2执行完毕。

OperationCanceledException被抛出主线程,因为您要求主线程等待任务t2并传递取消令牌。因此,通过捕获此异常,您的主线程不再等待任务t2完成,但这并不意味着任务t2将被停止。它将完全执行。此外,主线程现在可以继续执行其进一步的执行。

这让我得出另一个结论,即Console.ReadKey()方法可能会遗漏Main。这就是为什么你的控制台应用程序在异常被抛出后关闭的原因(主线程不再在任务t2上等待)。因此,您永远不会在控制台上看到Finish打印(假设您使用ctr < Int32.MaxValue运行循环)。

如果您希望停止任务t2,可以调用token.ThrowIfCancellationRequested();,如果令牌IsCancellationRequested设置为真,将导致任务t2停止。

for (int ctr = 0; ctr < Int32.MaxValue; ctr++)
{
    token.ThrowIfCancellationRequested();
    if (ctr % 100000000 == 0)
       Debug.WriteLine(ctr.ToString());
}