2

http://msdn.microsoft.com/en-us/magazine/gg598924.aspx

예외가 WPF Dispatcher.Invoke에 전파되지 않는 이유는 무엇입니까?

작업 예외가 UI 스레드로 다시 전파되도록 허용하려면 어떻게합니까?

아래의 코드에서 나는 예외에 던져진 예외 사항을 LogException이 처리 할 UI 스레드로 되돌려 보내야합니다. 라인을 따라 어딘가에서 예외를 다시 던질 필요가 있다면 저와 잘 맞는 것입니다. 어쨌든. 어떻게해야합니까? 내 질문과 비슷하지만 내 앱과 관련된 답변이 표시되지 않습니다.

3 편집 :단순화 된 예제 게시

편집 2 :이것 좀 봐:http://msdn.microsoft.com/en-us/library/dd997415(v=vs.100).aspx

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Loaded += new RoutedEventHandler(MainWindow_Loaded);
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        FireAndForget();
        WaitOnTask();
    }

    private void FireAndForget()
    {
        Task t1 = Task.Factory.StartNew(() =>
        {
            Thread.Sleep(3000);
            throw new Exception("boo"); 
        });

        Task c1 = t1.ContinueWith((t) =>
            {
                // The app global exception handler will not catch this.

            }, TaskContinuationOptions.OnlyOnFaulted);

        //MessageBox.Show("Task is running");
    }


    private void WaitOnTask()
    {
        Task t1 = Task.Factory.StartNew(() =>
        {
            throw new Exception("boo");
        });

        try
        {
            t1.Wait();
        }
        catch (Exception ex)
        {
            // The app global exception handler will catch this:
            throw new Exception("Task", ex);
        }
    }
}



public partial class App : Application
{
    public App()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        Application.Current.DispatcherUnhandledException += new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(Current_DispatcherUnhandledException);
        //System.Threading.Tasks.TaskScheduler.UnobservedTaskException += new EventHandler<System.Threading.Tasks.UnobservedTaskExceptionEventArgs>(TaskScheduler_UnobservedTaskException);
    }

    void TaskScheduler_UnobservedTaskException(object sender, System.Threading.Tasks.UnobservedTaskExceptionEventArgs e)
    {
        LogException(e.Exception);
    }

    void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
    {
        LogException(e.Exception);
    }

    void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        LogException(e.ExceptionObject as Exception);
    }

    private void LogException(Exception ex)
    {
        // log it
        string error = "This app has encountered an unexpected error .  The error message is:" + Environment.NewLine + ex.Message + Environment.NewLine;
        Exception tmp = ex.InnerException;

        while (tmp != null)
        {
            error += "Inner exception is: " + Environment.NewLine + tmp.Message + Environment.NewLine;
            tmp = tmp.InnerException;
        }

        error += "Please press OK to exit.";
        MessageBox.Show(error, "Error");
        Environment.Exit(-1);
    }
}


  • 사용하지 않는 이유가 있습니까?async/await대신에ContinueWith? - Stephen Cleary
  • @StephenCleary 예 네 .net 4 앱입니다. - Sam
  • 너 고려해 봤어?Microsoft.Bcl.Async? - Stephen Cleary
  • @StephenCleary Stephen, 제안 해 주셔서 감사합니다. 내 앱은 매우 복잡하여 최후의 수단으로 그렇게하고 싶습니다. 내가 직접 물어 본 것을 성취 할 수있는 직접적인 방법이 있습니까? - Sam
  • 그것은 나에게 보인다async할 것이다정말앱을 간소화 할 수 있지만 몇 분 안에 답변을 게시 할 수 있습니다. - Stephen Cleary

5 답변


3

사용하면StartNew또는ContinueWith, 모든 예외는 반환 된Task.

마샬링 예외에는 두 가지 문제가 있습니다.

  1. Task.Exception귀하의 예외를AggregateException.
  2. 나중에 (예 : 다른 스레드에서) 예외를 throw하면 원래 호출 스택이 손실됩니다.

첫 번째 문제의 경우 일부 사용자는Flatten또는Handle직접 일할 회원AggregateException. 나는 예외 처리를 언 래핑하는 것을 선호한다.Task.Exception.InnerException대신에Task.Exception.

두 번째 문제의 경우 일부 사람들은 다른 예외에서이를 감싸서 해결하지만 다른 대안을 택했습니다. .NET 4.5 도입ExceptionDispatchInfo, 이는옳은이것을하는 방법. .NET 4.0에서는 다음과 같이 해킹 할 수 있습니다.

public static Exception Rethrow(this Exception ex)
{
  typeof(Exception).GetMethod("PrepForRemoting",
      BindingFlags.NonPublic | BindingFlags.Instance)
      .Invoke(ex, new object[0]);
  throw ex;
}


  • Stephen, 귀하의 메서드를 구현하고 내 코드를 수정했습니다 : t.Exception.Rethrow ()하지만 작동하지 않습니다. - Sam
  • & quot; 작동하지 않습니다 & quot;라는 정확히 무엇을 의미합니까? - Stephen Cleary
  • 내 예외 처리기가 호출되지 않았습니다. - Sam
  • 내가 한 말을 분명히 알 수 없습니다. 최소한의 재현으로 질문을 업데이트하십시오. - Stephen Cleary
  • 게시 된 변경 사항. - Sam

1

내가 여기서 뭔가를 놓치고 있는지 잘 모르겠지만, ContinueWith의 두 번째 매개 변수로 TaskScheduler.FromCurrentSynchronizationContext () UX 스레드에 마샬링됩니다.

좀 더 샘플을 원한다면 실제로 블로그 게시물을 썼습니다.http://www.briankeating.net/post/Why-I-love-the-Task-library

, KR 브라이언.


  • 인터페이스 스레드에서 코드가 마샬링되지만 연속에서 발생하는 모든 예외는 다르게 처리됩니다. 즉, 코드를 사용하여보고됩니다.TaskScheduler.UnobservedTaskException대신에Dispatcher.UnhandledException - ghord

0

이 질문에 대한 답은 다음에서 찾을 수 있습니다.http://blogs.msdn.com/b/pfxteam/archive/2009/05/31/9674669.aspx

기본적으로 두 가지 시나리오가 있습니다. 즉, 작업을 기다릴 수있는 상황과 화재 나 잊을 수없는 상황이 있습니다. 작업을 기다릴 수있는 상황에서 질문에 표시된대로 try 블록에 랩핑하고 오류를 다시 제기하십시오. 글로벌 앱 핸들러가이를 잡아낼 것입니다.

작업을 기다릴 수없는 곳에서는 수동으로 로거에 전화해야합니다. 오류를 잡아낼 응용 프로그램 수준 처리기가 없습니다. TaskScheduler.UnobservedTaskException이 발생할 가능성이 있습니다. 그러나 이벤트는 매우 상황이 좋고 깨지기 쉽고 좋은 옵션이 아닙니다.


0

코드에서 예외를 전달하려면Wait모든 작업에. 귀하가 다음과 같이 변경하면FireAndForget방법Exception중첩 된Task호출 스레드로 다시 전달됩니다.

    private void FireAndForget()
    {
        var tasks = new Task[2];
        tasks[0] = Task.Factory.StartNew(() =>
        {
            Thread.Sleep(3000);
            throw new Exception("boo");
        });

        tasks[1] = tasks[0].ContinueWith((t) =>
        {
            throw new Exception("nested boo", tasks[0].Exception);

        }, TaskContinuationOptions.OnlyOnFaulted);

        try
        {
            Task.WaitAll(tasks);
        }
        catch (AggregateException ex)
        {
            throw new Exception("Task", ex);
        }
    }

물론 이것은 더 이상 "화재와 잊기"방법이 아닙니다. 작업을 기다리는 것이 바람직하지 않은 경우 계속 내에서 로그 파일에 기록해야합니다.


  • 오른쪽, 또는 작업 [0] 안에 try / catch 블록을 추가하십시오. - Sam

-1

너는 할 수있다.await태스크 코드로부터 예외를받는 태스크의 완료.

try{
  await Task.Factory.StartNew(() => throw Exception("hello"));       
}catch{
  // will get exception here
}


  • 나는 사용할 수 없습니다. 넷 4. 그러나 내가 제공 한 예제에서 WaitAll을 사용하여 시도하고 응용 프로그램이 단순히 중단됩니다. - Sam

연결된 질문


관련된 질문

최근 질문