.NETで並列処理を使用しているときにThreadStaticデータが消去されると、私は理解したいと思います。Parallel:ThreadStaticプロパティの有効期間(.NET)
は、以下の(重くカットダウンを)考えてみましょうコード:
マイContextクラス
public class AppContext
{
[ThreadStatic]
private static Person _person;
public Person Shopper
{
get => AppContext._person;
set => AppContext._person = value;
}
}
- このクラスがそれに多くのプロパティを持っているでしょう - 確かに50
- すべての上プロパティのバッキングフィールドはThreadStaticフィールドです。
オブジェクト
var response = await Get100ItemsToProcess().ConfigureAwait(false);
var singleContext = new AppContext();
Parallel.ForEach(response.items, new ParallelOptions(), i =>
{
// Set all properties on the singleContext for this thread.
singleContext.Shopper = new Shopper { Name = ...., etc}
....
ProcessItem(i);
// Dispose of any IDisposable properties on the singleContext
....
});
の多数の並列処理注:
ProcessItem(...)は、単純な関数が、複雑な、多段階ではありません、同期プロセス、ほぼ "アプリケーション内のアプリケーション"。それは同期的であるため、処理中の項目に固有のデータを保持するためにThreadStaticプロパティを使用することがあります。
スレッド(たとえば managedThreadId = 24)が初めて並列ループに入ると、最初はsingleContext.Shopperがnullです。
このスレッド(managedThreadId = 24)がShopperを初期化すると、その買い物客はThreadStaticフィールドに保持され、したがって他のスレッドはアクセスできなくなります。
同じスレッド(managedThreadId = 24)が(別のアイテムを処理するために)ループに再び入ると、singleContext.Shopperは前のループでインスタンス化されたものと同じオブジェクトです。
だから、私の理解が(間違っている場合修正してください)ということです。私たちはParallel.ForEachループを作成するとき、それはとき
- これらのスレッドの1つがループを完了した後、新しいループを開始し、スタックは消去されません。最初の反復で設定されたすべてのThreadStatic変数は2番目の反復で残っています(ただし、上書きします)。
- Parallel.ForEachが完了したときのみ、スレッドプールに戻ります。
質問:これらのプロパティはいつメモリから削除されますか。スレッドがThreadPoolに返されたとき(おそらくParallel.ForEachが完了したとき)、またはスレッドが別のAppDomainに割り当てられたときにそれらがクリーンアップされますか?私はこれらのプロパティのいくつかは、多くのメモリを消費する可能性があり、彼らは彼らが必要とする以上の時間のためにメモリを狩りをしないようにしたいので、私は尋ねています。私は特にループの反復処理の終わりに、それらをnullに明示的に設定するという考えは好きではありません。
私は 'AppContext'クラスが解決策の一部であることは確かではありませんが、' ThreadStatic'ではなく 'ThreadLocal'を検討しています。使用後に明示的に 'Dispose 'することができます(または' using'でラップすることができます)。その後、寿命の鮮明な画像が得られます。 –
@Damien_The_Unbeliever:AppContextクラスは、アイテムの処理を開始するために必要なすべての情報を保持します。ウェブサイトの場合は、購入者またはサイト自体に関する情報である可能性があります。頻繁に使用されるデータを取得するための「ワンストップショップ」です。私はそれもキャッシュのようだが、構造的に異なると思う。 – DrGriff
@Damien_The_Unbeliever:ThreadLocalの使用に関して良い点があり、後でその点に移ります。リファクタリングを単純化するには、単一のAppContextのコンセプトを維持する必要がありますが、各プロパティのバッキングフィールドをThreadLocal にする必要があります。その場合、Parallelループ内の.Dispose()を呼び出して、現在のスレッドの値を破棄する必要があります。 –
DrGriff