3

내 문제 : .NET 4 및 WinForms 응용 프로그램에서 TPL을 사용하고 싶습니다.나는 기다리지 않고 처리되지 않은 예외를 즉각 ( "빨리 던지기") 승격시키는 작업 계속이 필요하다.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

연속체에서 예외가 처리되지 않으면 즉시 throw됩니다.Excecute방법.

.NET 4에서 동일한 동작을 달성하는 방법async/await지원하다?

2 답변


1

우선, async-await를 .Net 4.0과 함께 사용하는 것이 가능하다는 것을 알아야합니다.Microsoft.Bcl.Async

하지만이 기능이 없으면 다음 작업으로 계속할 수 있습니다.ContinueWith예외가있을 때만 실행하도록하십시오.TaskContinuationOptions.OnlyOnFaulted

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


  • 그건 내 질문에 답하지 못한다. 나는 fast-throw & quot; 처리되지 않은 예외. 어쩌면 나는 핸들러를 가진 예제 때문에 혼란을 가져 왔을 것이다. 그러나 핸들러가 없다고 상상해보십시오. 또는 모든 예외 처리기가 예외 자체를 발생시킬 가능성이 항상 있습니다. TPL이 UnobservedTaskException으로 삼키는 일없이 예외가 즉시 throw되기를 바란다. - mancze
  • @ mancze 던져 어디? 신청서를 찢어 버리시겠습니까? - i3arnon
  • 전체 호출 스택을 통해 예외를 높이거나 버블 링합니다. 제대로 처리 할 수있는 사용자 지정 처리기가있을 수 있습니다. 핸들러가 앱을 찢어 버리지 않으면, 그렇습니다. - mancze
  • @ mancze 대기중인 스레드가없는 한 거품을내는 작업에는 호출 스택이 없습니다. 작업을 차단할 수 있습니다.Task.Wait그러나 TPL을 사용하여 시작하는 것은 무의미합니다. - i3arnon
  • 알 수 있듯이, 특정 호출 스택이없는 작업에 대해서는 중요한 점이 있습니다. 내가 특정 스레드 (UI 스레드)에 작업을 예약하지 않으면. 부름Wait트릭을하지만, 내가 원하지 않는 UI 스레드를 차단합니다. 나는 & quot;Wait& quot; 예외가 발생하면 곧. 계속해서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목표에SynchronizationContext따라서 TPL의 예외 트래핑 메커니즘을 피할 수 있습니다. 그것은 빠르고 동기식에서 멀리 떨어져 있지만 적어도 그것은 작업 처리를 기다리는 것보다 더 잘 동작합니다.

관련된 질문

최근 질문