別のスレッドでスローされた例外をキャッチする


109

私のメソッドの1つ(Method1)が新しいスレッドを生成します。そのスレッドはメソッド(Method2)を実行し、実行中に例外がスローされます。呼び出しメソッドに関する例外情報を取得する必要があります(Method1

Method1スローされるので、この例外をキャッチできるのMethod2ですか?

回答:


181

では、.NET 4と上記の、あなたが使用することができTask<T>、新しいスレッドを作成するのではなく、クラスを。次に.Exceptions、タスクオブジェクトのプロパティを使用して例外を取得できます。それには2つの方法があります。

  1. 別のメソッドで://一部のタスクのスレッドで例外を処理します

    class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(Test);
            task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
            task.Start();
            Console.ReadLine();
        }
    
        static int Test()
        {
            throw new Exception();
        }
    
        static void ExceptionHandler(Task<int> task)
        {
            var exception = task.Exception;
            Console.WriteLine(exception);
        }
    }
  2. 同じメソッドで:// 呼び出し元のスレッドで例外を処理します

    class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(Test);
            task.Start();
    
            try
            {
                task.Wait();
            }
            catch (AggregateException ex)
            {
                Console.WriteLine(ex);    
            }
    
            Console.ReadLine();
        }
    
        static int Test()
        {
            throw new Exception();
        }
    }

あなたが得る例外はであることに注意してくださいAggregateException。すべての実際の例外は、ex.InnerExceptionsプロパティを通じて利用できます。

では.NET 3.5は、次のコードを使用することができます。

  1. // 子のスレッドで例外を処理します

    class Program
    {
        static void Main(string[] args)
        {
            Exception exception = null;
            Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), Handler));
            thread.Start();            
    
            Console.ReadLine();
        }
    
        private static void Handler(Exception exception)
        {        
            Console.WriteLine(exception);
        }
    
        private static void SafeExecute(Action test, Action<Exception> handler)
        {
            try
            {
                test.Invoke();
            }
            catch (Exception ex)
            {
                Handler(ex);
            }
        }
    
        static void Test(int a, int b)
        {
            throw new Exception();
        }
    }
  2. または// 呼び出し元のスレッドで例外を処理します

    class Program
    {
        static void Main(string[] args)
        {
            Exception exception = null;
            Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), out exception));
    
            thread.Start();            
    
            thread.Join();
    
            Console.WriteLine(exception);    
    
            Console.ReadLine();
        }
    
        private static void SafeExecute(Action test, out Exception exception)
        {
            exception = null;
    
            try
            {
                test.Invoke();
            }
            catch (Exception ex)
            {
                exception = ex;
            }
        }
    
        static void Test(int a, int b)
        {
            throw new Exception();
        }
    }

申し訳ありませんが、私は.NET 3.5を使用していることを忘れていました。私の理解によると、タスクは4.0のものですか?
Silverlight学生、

2
@SilverlightStudent OK、私はあなたの要件を満たすために私の答えを更新しました。
オキシルミン

@oxilumin:ありがとうございました。さらにもう1つ質問があります。Test()メソッドもいくつかの引数を取る場合、それらの引数のSafeExecuteメソッドをどのように変更しますか?
Silverlight学生、

2
@SilverlightStudentこの場合、の代わりにラムダを渡しますTest。のような() => Test(myParameter1, myParameter2)
オキシルミ

2
@SilverlightStudent:更新されました。
オキシルミン

9

Method1では例外をキャッチできません。ただし、Method2で例外をキャッチして、実行の元のスレッドが読み取って操作できる変数に記録することができます。


御返答いただき有難うございます。つまり、Method1がClass1の一部であり、そのクラスにException型の変数があるとします。Method2が例外をスローするときはいつでも、Class1にもその例外変数を設定します。公正なデザインのように聞こえますか?このシナリオを処理するベストプラクティスの方法はありますか?
Silverlight学生、

正解です。例外を保存して後でアクセスするだけです。将来実行されるメソッド(特に、Method2が完了したときのコールバック)が、その例外が原因であるかのようにその例外を再スローすることは珍しいことではありませんが、これは本当にあなたが望むものに依存します。
エルマウ

0

異なるスレッド間でデータを共有する最も簡単な方法はshared data次のとおりです(一部は擬似コードです)。

class MyThread
{
   public string SharedData;

   public void Worker()
   {
      ...lengthy action, infinite loop, etc...
      SharedData = "whatever";
      ...lengthy action...
      return;
   }
}

class Program
{
   static void Main()
   {
      MyThread m = new MyThread();
      Thread WorkerThread = new Thread(m.Worker);
      WorkerThread.Start();

      loop//or e.g. a Timer thread
      {
         f(m.SharedData);
      }
      return;
   }
}

この方法については、マルチスレッディングに関するこの素晴らしい紹介で読むことができますがO'Reilly book C# 3.0 in a nutshell、これについては、新しいバージョンの本と同じように、Googleブックスでも自由にアクセスできるAlbahari兄弟(2007)ので読むことをお勧めします。スレッドプーリング、フォアグラウンドスレッドとバックグラウンドスレッドなどもカバーしているため、わかりやすくシンプルなサンプルコードを使用しています。(免責事項:私はこの本の古くなったコピーを所有しています)

WinFormsアプリケーションを作成している場合、WinFormコントロールはスレッドセーフではないため、共有データの使用は特に便利です。コールバックを使用してワーカースレッドからWinFormコントロールにデータを戻すには、Invoke()そのコントロールをスレッドセーフにするためにメインUIスレッドに醜いコードが必要です。代わりに共有データとシングルスレッドSystem.Windows.Forms.Timerを使用すると、Intervalたとえば0.2秒と短いため、ワーカースレッドからコントロールに簡単に情報を送信できますInvoke


0

統合テストスイートからのコントロールを含むアイテムを使用したいので、STAスレッドを作成する必要があるという特定の問題がありました。私が最終的に作成したコードは次のとおりです。他の人が同じ問題を抱えている場合に備えて、ここに配置してください。

    public Boolean? Dance(String name) {

        // Already on an STA thread, so just go for it
        if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) return DanceSTA(name);

        // Local variable to hold the caught exception until the caller can rethrow
        Exception lException = null;

        Boolean? lResult = null;

        // A gate to hold the calling thread until the called thread is done
        var lGate = new ManualResetEvent(false);

        var lThreadStart = new ThreadStart(() => {
            try {
                lResult = DanceSTA(name);
            } catch (Exception ex) {
                lException = ex;
            }
            lGate.Set();
        });

        var lThread = new Thread(lThreadStart);
        lThread.SetApartmentState(ApartmentState.STA);
        lThread.Start();

        lGate.WaitOne();

        if (lException != null) throw lException;

        return lResult;
    }

    public Boolean? DanceSTA(String name) { ... }

これは、コードをそのまま貼り付けたものです。他の用途については、アクションまたは関数をパラメーターとして指定し、呼び出されたメソッドをハードコーディングする代わりに、スレッドで呼び出すことをお勧めします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.