C#任务工厂超时

时间:2013-05-17 09:00:54

标签: c# timeout task factory

我必须在一个线程中执行一个长进程操作,并继续将结果返回给一个函数。这是我的代码:

Task<ProductEventArgs>.Factory.StartNew(() =>
    {
        try
        {
             // long operation which return new ProductEventArgs with a list of product

        }
        catch (Exception e)
        {
            return new ProductEventArgs() { E = e };
        }

    }).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());

问题实际上是我没有超时。我想放一个计时器,以便返回这样的东西:

   new ProductEventArgs() { E = new Exception("timeout") }; 

如果达到超时。 不能使用await / async。 非常感谢!

5 个答案:

答案 0 :(得分:14)

您应该使用CancellationToken s:

var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
var token = cts.Token;
Task<ProductEventArgs>.Factory.StartNew(() =>
{
    try
    {
        // occasionally, execute this line:
        token.ThrowIfCancellationRequested();
    }
    catch (OperationCanceledException)
    {
        return new ProductEventArgs() { E = new Exception("timeout") };
    }
    catch (Exception e)
    {
        return new ProductEventArgs() { E = e };
    }

}).ContinueWith((x) => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());

答案 1 :(得分:5)

此代码执行您在此处表达的内容:

var timeout = TimeSpan.FromSeconds(5);

var actualTask = new Task<ProductEventArgs>(() =>
{
    var longRunningTask = new Task<ProductEventArgs>(() =>
    {
        try
        {
            Thread.Sleep(TimeSpan.FromSeconds(10)); // simulates the long running computation
            return new ProductEventArgs();
        }
        catch (Exception e)
        {
            return new ProductEventArgs() { E = e };
        }
    }, TaskCreationOptions.LongRunning);

    longRunningTask.Start();

    if (longRunningTask.Wait(timeout)) return longRunningTask.Result;

    return new ProductEventArgs() { E = new Exception("timed out") };
});

actualTask.Start();

actualTask.Wait();

Console.WriteLine("{0}", actualTask.Result.E); // handling E

如您所见longRunningTask是使用TaskCreationOptions.LongRunning选项创建的。这样它就会有一个专用的Thread用于它的执行,并且不会通过从那里占用一个线程太长时间来干扰ThreadPool的正常行为 - 这将是其他事情所需要的即UI。 对于长时间运行的任务非常重要

注意:然后您可以使用actualTask处理ContinueWith,但我想在此处表达精华。

答案 2 :(得分:3)

您可以将返回的任务对象用于StartNew方法,然后使用用户Wait方法来确定超时。

Task<ProductEventArgs> task = Task<ProductEventArgs>.Factory.StartNew(() => {...});
if (!Task.Wait(new TimeSpan(0,0,1,0)) // wait for 1 minute
{
   // throw exception or something else if timeout
}

答案 3 :(得分:1)

您可以并行运行Task.Delay(timeout)任务并检查首先完成的任务(Task.WhenAny()在这种情况下非常方便):

public void FetchProduct(TimeSpan timeout)
{
    var fetchTask = Task<ProductEventArgs>.Factory.StartNew(
        () =>
        {
            try
            {
                // long operation which return new ProductEventArgs with a list of product
            }
            catch(Exception e)
            {
                return new ProductEventArgs() { E = e };
            }
        });
    Task<ProductEventArgs> resultTask;
    if(timeout != Timeout.InfiniteTimeSpan)
    {
        var timeoutTask = Task.Delay(timeout);
        resultTask = Task.WhenAny(resultTask, timeoutTask).ContinueWith<ProductEventArgs>(
            t =>
            {
                // completed task is the result of WhenAny
                if(t.Result == fetchTask)
                {
                    return fetchTask.Result;
                }
                else
                {
                    return new ProductEventArgs() { E = new TimeoutException() };
                }
            });
    }
    else
    {
        resultTask = fetchTask;
    }
    resultTask.ContinueWith(x => handleResult(x.Result), TaskScheduler.FromCurrentSynchronizationContext());
}

请注意,此解决方案没有任何取消逻辑,即使超时,您的长时间运行任务仍将继续运行。

答案 4 :(得分:0)

在主要任务(代理人)中启动另一项任务:

Task.Factory.StartNew(() => 
        {
            // returns a string result
            var tsk = new Task<string>(() => { return VeryImportantThingsToDo(); });
            try
            {
                tsk.Start();
                if (!tsk.Wait(5000))
                    throw new TimeoutException();
                return tsk.Result;
            }
            catch (TimeoutException)
            {
                // Jabba Dabba Doooooooohhhhhh
            }

            return "<unknown>";
        }).ContinueWith((o) => string result = o.Result));