4.5中的异步:取消和超时

时间:2013-04-16 17:23:10

标签: asp.net vb.net async-await asp.net-4.5

我在HTTPHandler中有一个长时间运行的进程,我使用Async Await异步执行.net 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 个答案:

答案 0 :(得分: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

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

Async in 4.5: Enabling Progress and Cancellation in Async APIs

答案 1 :(得分: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();
}

答案 2 :(得分:0)

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

因此,不是使用CancellationToken(Source),而是将超时代码视为另一个生成字符串的任务,然后选择任何任务(超时任务或“实际”任务)完成的任务,这可能更简单。返回那个字符串。

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

'Await Await'可能看起来很奇怪,但是Task.WhenAny返回一个Task(Of Task(Of T)),所以第一个Await是到达先完成的特定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