例外をスローする次のコードがあります。
ThreadPool.QueueUserWorkItem(state => action());
アクションが例外をスローすると、プログラムがクラッシュします。この状況に対処するためのベストプラクティスは何ですか?
action
のソースコードにアクセスできる場合は、そのメソッドにtry/catchブロックを挿入します。それ以外の場合は、tryAction
への呼び出しをtry/catchブロックでラップする新しいaction
メソッドを作成します。
次のようにtry/catchを追加できます。
ThreadPool.QueueUserWorkItem(state =>
{
try
{
action();
}
catch (Exception ex)
{
OnException(ex);
}
});
.Net 4.0を使用している場合は、 Task クラスを調査する価値があるかもしれません。これは、これを処理できるからです。
元のコードと同等ですが、タスクを使用すると、次のようになります。
Task.Factory.StartNew(state => action(), state);
例外を処理するために、StartNewによって返されるタスクに継続を追加できます。次のようになります。
var task = Task.Factory.StartNew(state => action(), state);
task.ContinueWith(t =>
{
var exception = t.Exception.InnerException;
// handle the exception here
// (note that we access InnerException, because tasks always wrap
// exceptions in an AggregateException)
},
TaskContinuationOptions.OnlyOnFaulted);
もう一方のスレッドで(「キューイング」しているメソッドで、try catch句を追加します。。次に、catchで、キャッチされた例外を共有Exception変数(メインスレッドに表示)に配置します。
次に、メインスレッドで、キューに入れられたすべてのアイテムが終了したら(これには待機ハンドル配列を使用します)、一部のスレッドがその共有例外に例外を設定したかどうかを確認します...設定した場合は、再スローするか、必要に応じて処理します...
これは私がこれを使用した最近のプロジェクトからのサンプルコードです...
HasExceptionは共有ブール値です...
private void CompleteAndQueuePayLoads(
IEnumerable<UsagePayload> payLoads, string processId)
{
List<WaitHandle> waitHndls = new List<WaitHandle>();
int defaultMaxwrkrThreads, defaultmaxIOThreads;
ThreadPool.GetMaxThreads(out defaultMaxwrkrThreads,
out defaultmaxIOThreads);
ThreadPool.SetMaxThreads(
MDMImportConfig.MAXCONCURRENTIEEUSAGEREQUESTS,
defaultmaxIOThreads);
int qryNo = 0;
foreach (UsagePayload uPL in payLoads)
{
ManualResetEvent txEvnt = new ManualResetEvent(false);
UsagePayload uPL1 = uPL;
int qryNo1 = ++qryNo;
ThreadPool.QueueUserWorkItem(
delegate
{
try
{
Thread.CurrentThread.Name = processId +
"." + qryNo1;
if (!HasException && !uPL1.IsComplete)
IEEDAL.GetPayloadReadings(uPL1,
processId, qryNo1);
if (!HasException)
UsageCache.PersistPayload(uPL1);
if (!HasException)
SavePayLoadToProcessQueueFolder(
uPL1, processId, qryNo1);
}
catch (MeterUsageImportException iX)
{
log.Write(log.Level.Error,
"Delegate failed " iX.Message, iX);
lock (locker)
{
HasException = true;
X = iX;
foreach (ManualResetEvent
txEvt in waitHndls)
txEvt.Set();
}
}
finally { lock(locker) txEvnt.Set(); }
});
waitHndls.Add(txEvnt);
}
util.WaitAll(waitHndls.ToArray());
ThreadPool.SetMaxThreads(defaultMaxwrkrThreads,
defaultmaxIOThreads);
lock (locker) if (X != null) throw X;
}
私が通常行うことは、大きなtry ... catchブロックをaction()メソッド内に作成し、例外をプライベート変数として格納してから、メインスレッド内で処理することです。
単純なコード:
public class Test
{
private AutoResetEvent _eventWaitThread = new AutoResetEvent(false);
private void Job()
{
Action act = () =>
{
try
{
// do work...
}
finally
{
_eventWaitThread.Set();
}
};
ThreadPool.QueueUserWorkItem(x => act());
_eventWaitThread.WaitOne(10 * 1000 * 60);
}
}