0

尝试学习任务、等待和取消。
从我认为来自 MSDN 的一个简单示例开始。
Task.Wait 方法(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 is unhandled by user code

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);

好的,我知道这越来越长
了 来自 Microsoft 的那个样本充其量是误导 - 为什么你会有一个没有真正取消的 Cancel 样本
来验证为什么 Rohit 说我添加了以下内容
果然我可以在新闻发布前看到更多的通行证任何继续的键

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

2 回答 2

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);
});
于 2014-03-16T20:15:56.687 回答
1

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

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

我怀疑在您测试的控制台应用程序中,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();这将导致任务 t2 停止,如果IsCancellationRequested将令牌设置为 true。

for (int ctr = 0; ctr < Int32.MaxValue; ctr++)
{
    token.ThrowIfCancellationRequested();
    if (ctr % 100000000 == 0)
       Debug.WriteLine(ctr.ToString());
}
于 2014-03-16T20:16:30.573 回答