3

私の問題:私は.NET 4のWinFormsアプリケーションでTPLを使いたい待っているのではなく、未処理の例外を即座に上げる( "fast throw")ためのタスク継続が必要です。GCを集めるTask出来ますか?

.NET 4.5ではasync/awaitそれは書くことが可能ですサポート:

Public Class AwaitForm
    Inherits Form

    Private Async Sub Execute()
        Dim uiScheduler = TaskScheduler.FromCurrentSynchronizationContext()

        Try
            Await Me.LongWork().
                ContinueWith(Sub(t) Me.LongWorkCompleted(), uiScheduler)

        Catch ex As Exception
            ' yay, possible to handle here
            ' eg. MsgBox(ex.Message)
            Throw
        End Try
    End Sub

    Private Async Function LongWork() As Task
        Await Task.Delay(1000)
    End Function

    Private Sub LongWorkCompleted()
        Throw New Exception("Ups")
    End Sub

End Class

で処理されないと、継続中の例外がただちにスローされます。Excecute方法。

なしで.NET 4で同じ動作を実現する方法async/awaitサポート?

2 답변


1

まず最初に、.Net 4.0でasync-awaitを使用することが可能であることを知っておくべきです。Microsoft.Bcl.Async

しかし、それがなければ、次のようにしてタスクに継続を追加できます。ContinueWithそして、例外があったときだけそれを走らせますTaskContinuationOptions.OnlyOnFaulted

Me.LongWork().ContinueWith(Sub(task) MsgBox(task.Exception.Message), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted)


  • それは私の質問には答えない - 私は「早送り」をしたかった。未処理の例外ハンドラを使った例のため、多少混乱したかもしれません。しかし、ハンドラがないと想像してください。あるいは、例外ハンドラが例外自体を発生させる可能性が常にあります。 TPLがUnobservedTaskExceptionとしてそれらを飲み込むことなく、私はそれらの例外が直ちに投げられることを望みます。 - mancze
  • @manczeはどこに投げた?あなたはアプリケーションを破棄したいですか? - i3arnon
  • コールスタック全体を通して例外を昇格させてバブル化する。それを適切に処理する方法で私のカスタムハンドラがあるかもしれません。ハンドラーがアプリを壊さずに、はい。 - mancze
  • @manczeタスクを待っているスレッドが存在しない限り、バブルアップするタスクの呼び出しスタックはありません。あなただけでタスクをブロックすることができますTask.Waitしかし、それからTPLを使用して始めても意味がありません。 - i3arnon
  • わかりました、あなた自身が特定の呼び出しスタックを持っていないタスクについての指摘を得ました。タスクを特定のスレッド(uiスレッド)にスケジュールしない限り。呼び出しWaitトリックを行いますが、私はしたくないUIスレッドをブロックします。 "したいWait"例外がスローされるとすぐに、と継続しているWait繰り返しますが、未処理の例外はTPLによってトラップされます。 - mancze

1

1)どちらでも使えますMicrosoft.Bcl.Asynci3arnonが示唆しているように。

2)または、あなたが追加のライブラリを参照したくないならば、私はに基づく解決策を思い付きましたasync/await。背後にある魔法は幸運ですが、それは私が持っていることが最高です。

Imports System.Reflection
Imports System.Runtime.CompilerServices
Imports System.Threading


Public Module TaskExtensions

    ''' <summary>Throws the exception on the current SynchronizationContext or ThreadPool if there is none.</summary>
    ''' <param name="task">Task whose faulted continuation should throw exception.</param>
    <Extension()>
    Public Sub ThrowOnFaulted(task As Task)
        Dim context = SynchronizationContext.Current
        ThrowOnFaulted(task, context)
    End Sub


    ''' <summary>Throws the exception on the ThreadPool in given context.</summary>
    ''' <param name="task">Task whose faulted continuation should throw exception.</param>
    ''' <param name="targetContext">The target context on which to propagate the exception. Null to use the ThreadPool.</param>
    <Extension()>
    Public Sub ThrowOnFaulted(task As Task, targetContext As SynchronizationContext)
        task.ContinueWith(Sub(t) ThrowOnFaultedCore(t, targetContext), TaskContinuationOptions.OnlyOnFaulted)
    End Sub


    ''' <remarks>Taken from System.RunTime.CompilerServices.AsyncServices.</remarks>
    Private Sub ThrowOnFaultedCore(task As Task, targetContext As SynchronizationContext)
        Dim exception = task.Exception

        If targetContext IsNot Nothing Then
            Try
                targetContext.Post(Sub(state) Throw DirectCast(state, Exception), exception)
                Return
            Catch ex As Exception
                exception = New AggregateException({exception, ex})
            End Try
        End If

        ThreadPool.QueueUserWorkItem(Sub(state) Throw DirectCast(state, Exception), exception)
    End Sub

End Module

それは要件を満たしています - 例外は "速く"投げられそして処理することができます。例外はPostターゲットにedSynchronizationContextしたがって、TPLの例外トラップメカニズムを回避します。高速で同期的ではありませんが、少なくともタスクの破棄を待つよりも動作が優れています。

関連する質問

最近の質問