2013-04-16 84 views
2

我裏面的HttpHandler一個長期運行的進程,我異步執行使用異步等待.NET 4.5異步在4.5:取消和超時

這工作完全按預期...

我怎樣才能添加超時,以便在過程耗時過長時返回字符串'timeout'?

Protected Async Sub button1_Click(sender As Object, e As System.EventArgs) 
    Dim asyncHandler = New AsyncHandler 
    Await asyncHandler.ProcessRequestAsync(HttpContext.Current) 
    Response.Write(asyncHandler.Result) 
End Sub 

Public Class AsyncHandler 
    Inherits HttpTaskAsyncHandler 

    Public Property Result As String 

    Public Async Function ProcessRequestAsync(context As HttpContext) As Task 
     Me.Result = Await DoLongRunningProcessAsync(context) 
    End Function 

    Private Function DoLongRunningProcessAsync(context As HttpContext) As Task(Of String) 

     'TODO: add a timeout so that if this takes too long we return "timeout": 

     Return Task.Run(Of String)(Function() DoLongRunningProcess(context)) 

    End Function 

    Private Function DoLongRunningProcess(context As HttpContext) As String 
     'perform long running process.... 

     Return "success" 
    End Function 

End Class 

回答

3

這是我終於實現:

創建一個的CancellationToken了超時值x毫秒

增加了一個嘗試catch捕獲錯誤:OperationCanceledException

Public Async Function ProcessRequestAsync(context As HttpContext) As Task 
     Dim cts As CancellationTokenSource = New CancellationTokenSource(30000) 'eg: 30 seconds 
     Try 
      Me.ResultCode = Await DoLongRunningProcessAsync(HttpContext.Current, cts.Token) 
     Catch ex As OperationCanceledException 
      Me.ResultCode = "timeout" 
     End Try 
End Function 

將CancellationToken傳遞給DoLongRunningProcessAsync方法的簽名

通過調用ThrowIfCancellationRequested

Private Function DoLongRunningProcessAsync(context As HttpContext, ct As CancellationToken) As Task(Of String) 
     Return Await Task.Run(Of String)(
      Function() 
       Dim resultCode As String = DoLongRunningProcess(context) 

       'detect timeout: 
       ct.ThrowIfCancellationRequested() 

       Return resultCode 
      End Function) 
End Function 

這是一個偉大的文章,我發現,真正幫助檢測超時:

Async in 4.5: Enabling Progress and Cancellation in Async APIs

2

我不結VB.NET,但我通常例如低於在執行此操作在C#等:

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); 

// Make sure that we have a way to cancell long running 
Task<SomeClass> longRunningTask = GetSomethingAsync(cancellationTokenSource.Token); 

// One of the task should be finished 
if (longRunningTask == await Task.WhenAny(longRunningTask, Task.Delay(30000))) 
{ 
    // Long running task completed 
    SomeClass result = await longRunningTask; 
} 
else 
{ 
    // Task.Delay(30000) was finished 
    cancellationTokenSource.Cancel(); 
} 
+3

'CancellationTokenSource'可以採取一個時間跨度,構造函數的參數,這大大簡化了這種方法。 –

+1

+1 @StephenCleary - 還有一個典型的'毫秒作爲int'超載可用 - http://msdn.microsoft.com/en-us/library/system.threading.cancellationtokensource.cancellationtokensource.aspx –

0

通常TPL代碼支撐超時通過如所提到的CancellationToken(來源)這樣做@outcoldman和@StephenCleary。但是,在你的場景中,你說你想要超時路徑也返回一個字符串,只是一個不同的字符串。

因爲這個原因,而不是使用CancellationToken(Source),它可能會更簡單,只是將您的超時代碼視爲另一個產生字符串的任務,然後選擇任一任務(超時任務或「真實」任務)並返回該字符串。

我將您的DoLongRunningProcess(最內層函數)更改爲async並返回Task(Of String),這樣您就不必Task.Run了。

'等待等待'可能看起來很奇怪,但Task.WhenAny會返回一個Task(Of Task(Of T)),所以第一個等待是先到達完成的特定Task,然後我們按順序等待得到實際的字符串結果。

Sub Main 
    MainAsync().Wait() 
End Sub 

' Define other methods and classes here 
Public Async Function MainAsync As Task 
    Dim asyncHandler = New AsyncHandler 
    Await asyncHandler.ProcessRequestAsync(System.Web.HttpContext.Current) 
    Console.WriteLine(asyncHandler.Result) 
End Function 

Public Class AsyncHandler 
    Inherits HttpTaskAsyncHandler 

    Public Property Timeout As TimeSpan = TimeSpan.FromSeconds(10) 

    Public Property Result As String 

    Public Overrides Async Function ProcessRequestAsync(context As HttpContext) As Task 
     Me.Result = Await DoLongRunningProcessAsync(context) 
    End Function 

    Private Async Function DoLongRunningProcessAsync(context As HttpContext) As Task(Of String) 

     'TODO: add a timeout so that if this takes too long we return "timeout": 

     Dim workerTask As Task(Of String) = DoLongRunningProcess(context) 
     Dim cancelTask As Task(Of String) = DoTimeout() 

     Return Await Await Task.WhenAny(workerTask, cancelTask) 

    End Function 

    Private Async Function DoTimeout() As Task(Of String) 
     Await Task.Delay(Timeout) 

     Return "timeout" 
    End Function 

    Private Async Function DoLongRunningProcess(context As HttpContext) As Task(Of String) 
     'perform long running process.... 

     Await Task.Delay(TimeSpan.FromSeconds(15)) 

     Return "success" 
    End Function 

End Class 
+0

偉大的替代方法 - 但實際上應該使用取消標記 –

+0

同意@ geo1701。 CancellationToken應該受到青睞,因爲在某些情況下,上述方法實際上可能會導致Web服務器崩潰。 – Levi