3

Async Await .net 4.5 を使用して非同期的に実行する HTTPHandler 内に長時間実行されるプロセスがあります。

これは期待どおりに完全に機能します...

プロセスに時間がかかりすぎる場合に文字列 '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
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 は最初に終了した特定のタスクを取得することであり、次に実際のタスクを取得するためにそれを 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 に答える