【问题标题】:How to cancel a task that is waiting with a timeout without exceptions being thrown如何取消等待超时的任务而不抛出异常
【发布时间】:2011-10-04 15:40:12
【问题描述】:

使用取消令牌取消具有超时(在超时结束之前)的任务时,会引发异常。示例:

mytask.start();
bool didTaskRunInTime = mytask.wait(5 mins, _cancelToken);

这意味着我不能像下面那样继续。

//was the task cancelled
if (_cancelToken.IsCancelRequested)
{
    // log cancel from user to file etc
}

if (didTaskRunInTime )
{
    int taskResult = myTask.Result;
    // log result to file
}
else if (!_cancelToken.IsCancelRequested)
{
    // Tell user task timed out , log a message etc
}

我将不得不在我的 catch 块中执行所有这些操作,我的代码看起来很乱。这样做的正确方法是什么?

【问题讨论】:

    标签: c# multithreading c#-4.0 task-parallel-library pfx


    【解决方案1】:

    您可以调用Task.WaitAny 并带有一个只是该任务的数组。然后你可以对任务的状态采取行动,但是方法会返回。示例代码:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class Test
    {
        static void Main()
        {
            Task sleeper = Task.Factory.StartNew(() => Thread.Sleep(100000));
    
            int index = Task.WaitAny(new[] { sleeper },
                                     TimeSpan.FromSeconds(0.5));
            Console.WriteLine(index); // Prints -1, timeout
    
            var cts = new CancellationTokenSource();
    
            // Just a simple wait of getting a cancellable task
            Task cancellable = sleeper.ContinueWith(ignored => {}, cts.Token);
    
            // It doesn't matter that we cancel before the wait
            cts.Cancel();
    
            index = Task.WaitAny(new[] { cancellable },
                                 TimeSpan.FromSeconds(0.5));
            Console.WriteLine(index); // 0 - task 0  has completed (ish :)
            Console.WriteLine(cancellable.Status); // Cancelled
        }
    }
    

    请注意,如果任务出错,您应该“观察”异常以避免它在完成时发生爆炸:)

    【讨论】:

    • 我认为它会抛出,WaitAny 将聚合所有异常,您甚至应该在 waitAny 之后调用 wait(并且可能取消其他异常)以捕获它们的异常。
    • 谢谢约翰。但是waitany不会超时。我有长时间运行的导出需要超时处理。
    • @eFloh:WaitAll 聚合异常。 WaitAny 没有。请参阅示例代码。
    • @Gullu:是的(或者至少可以)——请参阅我的示例代码 :)
    • @Jon Skeet:很高兴知道,这实际上意味着在使用 waitAny 之后,必须确保捕获所有其他任务的异常(例如使用 WaitAll)
    【解决方案2】:

    尝试使用 OperationCanceledException

    try
    {           
        mytask.start();
        bool didTaskRunInTime = mytask.wait(5 mins, _cancelToken);
    
        if (didTaskRunInTime )
        {
            int taskResult = myTask.Result;
            //log result to file
        }
        else
        {
            // Tell user task timed out , log a message etc
        }
    }
    catch (OperationCanceledException ex)
    {
        // log cancel from user to file et
    }
    

    【讨论】:

    • 那是我试图避免的,但我可能别无选择。我的理由是,如果有人设置了一个 cancelToken 并打算取消,为什么这个东西是为了抛出异常而设计的。谢谢弗拉德
    • 这里不需要使用异常 - 请参阅我的答案以获取完全没有 try/catch 的示例。
    猜你喜欢
    • 2018-07-07
    • 1970-01-01
    • 1970-01-01
    • 2014-04-09
    • 2014-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多