メソッドの1つ(Method1
)が新しいスレッドを生成します。そのスレッドはメソッド(Method2
)を実行し、実行中に例外がスローされます。呼び出しメソッドの例外情報を取得する必要があります(Method1
)
Method1
でスローされるMethod2
でこの例外をキャッチできる方法はありますか?
。NET 4以上では、新しいスレッドを作成する代わりにTask<T>
クラスを使用できます。次に、タスクオブジェクトの.Exceptions
プロパティを使用して例外を取得できます。それを行うには2つの方法があります。
別の方法で://いくつかの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);
}
}
同じメソッドで:// 呼び出し側スレッドで例外を処理します
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では、次のコードを使用できます。
//例外を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();
}
}
または// 呼び出し側スレッドで例外を処理します
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();
}
}
Method1で例外をキャッチすることはできません。ただし、Method2で例外をキャッチし、元の実行スレッドが読み取って操作できる変数に記録できます。
異なるスレッド間でデータを共有する最も簡単な方法は、次の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
なしでワーカースレッドからコントロールに情報を簡単に送信できます。
統合テストスイートのコントロールを含むアイテムを使用するため、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) { ... }
これは、コードをそのまま貼り付けたものです。他の用途では、呼び出されたメソッドをハードコーディングする代わりに、アクションまたは関数をパラメーターとして提供し、スレッドで呼び出すことをお勧めします。