2013-05-17 45 views
9

我必须在一个线程中执行一个长进程操作并继续将结果返回给一个函数。这里是我的代码:C#任务工厂超时

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。 非常感谢!

回答

4

此代码,你在这里所表达的内容:

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与​​选项创建的。这样一来,它将有一个专门的Thread执行,并且不会妨碍ThreadPool的正常行为,因为它占用的线程时间过长 - 这对于其他事情(例如用户界面)将需要。 这对长时间运行的任务很重要。

注意:然后您可以用ContinueWith处理actualTask,但我想在此处表达其实质。

+1

爱你: - *我在C#有点新的,更多的线程管理,你让我的天! – Rototo

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

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

12

你应该使用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()); 
+0

我完全同意这一点。希望OP可以做到这一点 - 我根据OP(可能是皮疹)的假设回答了这个问题,即OP正在调用一些不支持超时或取消的控制的代码,所以我认为他不能这样做。我希望他能。 :) –

+1

在你的代码中,我只需要将token作为第二个参数给startnew方法,对吧?因为你没有写。如果我做到了,并且例如有一个0秒的时间,我对句柄结果(x.Result)有一个aggregateException。谢谢 – Rototo

+0

你不应该在你的情况下将'CancellationToken'传递给'StartNew'。如果你这样做(如你所见),一个已经取消的'CancellationToken'实际上会在开始之前取消这个任务。但是你不希望任务完成取消,所以这不是你想要的行为。 –

3

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

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