2013-05-17 99 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));