3

我在 HTTPHandler 中有一个长时间运行的进程,我使用 Async Await .net 4.5 异步执行

这可以按预期完美运行...

如何添加超时,以便在处理时间过长时返回字符串“超时”?

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
4

3 回答 3

4

这是我最终实现的:

创建了一个超时值 x 毫秒的 CancellationToken

添加了一个 try 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

这是一篇很棒的文章,我发现它真的很有帮助:

4.5 中的异步:在异步 API 中启用进度和取消

于 2013-04-17T11:03:46.633 回答
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();
}
于 2013-04-16T17:54:03.540 回答
0

通常,支持超时的 TPL 代码通过 @outcoldman 和 @StephenCleary 提到的 CancellationToken(Source) 来实现。但是,在您的场景中,您说您希望超时路径也返回一个字符串,只是一个不同的字符串。

因此,与其使用 CancellationToken(Source),不如将超时代码视为另一个生成字符串的任务,然后选择首先完成的任务(超时任务或“真实”任务)并返回该字符串,这可能会更简单.

我将您的 DoLongRunningProcess (最里面的函数)更改为异步并返回 Task(Of String) ,这样您就不必执行 Task.Run 了。

'Await Await' 可能看起来很奇怪,但是 Task.WhenAny 返回一个 Task(Of Task(Of T)) 所以第一个 Await 是到达首先完成的特定任务,然后我们等待它以获得实际的字符串结果。

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
于 2013-04-17T02:11:05.467 回答