TPLで集計エクセプトを引き起こす方法は?
-
12-10-2019 - |
質問
この例外を引き起こす条件を再現しようとしています。
System.AggregateException: A Task's exception(s) were not observed
either by Waiting on the Task or accessing its Exception property.
As a result, the unobserved exception was rethrown by the finalizer thread.`
私は例外を引き起こすと考えてこのプログラムを書きましたが、そうではありません。
using System;
using System.Threading.Tasks;
namespace SomeAsyncStuff
{
class Program
{
static void Main(string[] args)
{
Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); });
GC.Collect();
Console.WriteLine("completed");
}
}
}
実際のアプリケーションでは、TPLを使用していますが、例外を正しくコード化しませんでした。その結果、私はその例外を取得します。今、私は、観測されていない例外を実験するために、別のプログラムで同じ条件を再現しようとしています。
解決 5
私はOPです。 gc.waitforpendingfinalizers()をテストしましたが、例外を再現するのに役立ちませんでした。問題は、タスクが開始される前にGC.COLLECT()が実行されたことでした。
これは、例外を再現するための正しいコードです。
using System;
using System.Threading;
using System.Threading.Tasks;
namespace SomeAsyncStuff
{
class Program
{
static void Main(string[] args)
{
Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); });
// give some time to the task to complete
Thread.Sleep(3000);
GC.Collect();
// GC.WaitForPendingFinalizers();
Console.WriteLine("completed");
}
}
}
他のヒント
ファイナライザーが独自のスレッドで実行されているため、GC.Collect()後にGC.WaitForPendingFinalizers()に呼び出しを追加する必要がある場合があります。
例外はによってスローされます TaskExceptionHolder
のファイナライザーなので、この例外がスローされる前にファイナイザースレッドを実行する必要があります。ジョシュが指摘しているように、あなたはそれが起こるのを待つことができます CG.WaitForPedingFinalizers()
.
この動作は現在のASYNC CTPで変更されていることに注意してください。 PFXチームのStephen Toubに、今年初めにTeched Europeでこれについてこれについて話しましたが、彼は新しい非同期機能が正しく機能するためにそれを変更しなければならないことを示しました。そのため、フレームワークの次のバージョンについて何かを言うのはまだ時期尚早ですが、この動作は今後のバージョンで非常によく変更される可能性があります。
@sly、あなたは実用的な答えを思いつきましたが、ほとんどの人はエラーメッセージの提案に耳を傾けることでより良いサービスを提供すると思います。 GCアクティビティに参加することは、GCを密接に知っており、パフォーマンスボトルネックを持っているか、ポイントが欠けていることを示す兆候です。私の場合、それは後者を意味します;)StartNew Callはタスクを返しますので、なぜそれを使用してみませんか?例えば
タスクmytask = task.factory.startnew(()=> {nullReferenceException( "ex");}); //完了するためのタスクに時間を与えます
mytask.wait();
タスクの完了後に適切にコードを呼び出そうとしなかったことに本当に驚いています。それだけでなく、3秒間他のプロセスを結び付けます。タスクが完了した後、その実装を継続()タスクメソッドに置き換えてGCを呼び出します。
Task.Factory
.StartNew(() => { throw new NullReferenceException("ex"); })
.ContinueWith(p => GC.Collect());
完了するまでブロックが必要な場合(デバッグするために使用していたコードの例では)、タスクを起動した後に待機中の実行を行い、待機ハンドラーを[)継続的に()継続することもできます。本番コードでこれを行わなければならない場合は、達成しようとしていることは、実際にタスクを使用することを心配する必要がない場合に実際に同期しています。
エラーを再現する最も簡単な方法は、タスクが完了するのを待つことです。
Task task = Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); });
//this is where the exception will be thrown
task.Wait();
通話待機は、タスクが実行が完了するまでコールをブロックします。