web-dev-qa-db-ja.com

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

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

Method1でスローされるMethod2でこの例外をキャッチできる方法はありますか?

105

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

  1. 別の方法で://いくつかのtask'sスレッドで例外を処理します

    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. //例外をchild'sスレッドで処理します

    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();
        }
    }
    
175
oxilumin

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

9
ermau

異なるスレッド間でデータを共有する最も簡単な方法は、次の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で読むことをお勧めします。これは、兄弟Albahari(2007)からもアクセスできます。 Googleブックスは、新しいバージョンの本と同じように、ナイスでシンプルなサンプルコードでスレッドプーリング、フォアグラウンドスレッドとバックグラウンドスレッドなどをカバーしているためです。 (免責事項:私はこの本の使い古されたコピーを所有しています)

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

0
Roland

統合テストスイートのコントロールを含むアイテムを使用するため、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) { ... }

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

0