-1

.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に明示的に設定するという考えは好きではありません。

  • +1

    私は 'AppContext'クラスが解決策の一部であることは確かではありませんが、' ThreadStatic'ではなく 'ThreadLocal 'を検討しています。使用後に明示的に 'Dispose 'することができます(または' using'でラップすることができます)。その後、寿命の鮮明な画像が得られます。 –

    +0

    @Damien_The_Unbeliever:AppContextクラスは、アイテムの処理を開始するために必要なすべての情報を保持します。ウェブサイトの場合は、購入者またはサイト自体に関する情報である可能性があります。頻繁に使用されるデータを取得するための「ワンストップショップ」です。私はそれもキャッシュのようだが、構造的に異なると思う。 – DrGriff

    +0

    @Damien_The_Unbeliever:ThreadLocal の使用に関して良い点があり、後でその点に移ります。リファクタリングを単純化するには、単一のAppContextのコンセプトを維持する必要がありますが、各プロパティのバッキングフィールドをThreadLocal にする必要があります。その場合、Parallelループ内の.Dispose()を呼び出して、現在のスレッドの値を破棄する必要があります。 – DrGriff

    答えて

    2

    ThreadStaticはスレッド固有の記憶領域です。ThreadStatic特性

    ThreadStatic

    寿命は、スレッド固有の記憶空間であるので、ThreadStaticプロパティオブジェクトの寿命は、スレッドの寿命です。

    我々はParallel.ForEachループを作成するとき、それは、スレッドプール

    Parallelからスレッドの一握りは、それよりも複雑で割り当てられます。実行中に必要に応じて使用するスレッドの数を調整できます。スレッドは、パラレルループの「所有権」が実行されるときに「入力」と「離脱」の両方を行うことができます。

    これらのスレッドの1がループを完了し、新しいループを開始したとき、それはそのスタックがきれいに持っていません - 最初の繰り返しで設定されたすべてのThreadStatic変数が2回目の反復

    のために残っています

    はい。それは "スタック"とは関係がありません。 ThreadStaticはスレッド固有の記憶領域なので、依然としてスレッドに関連しています。

    これらのプロパティはいつメモリから削除されますか?

    ThreadStaticはスレッド固有の記憶領域であるため、スレッドが完了するとクリーンアップされます。

    スレッドがThreadPoolに返されたときにクリーンアップされますか(Parallel.ForEachが完了したと思われます)?

    いいえスレッドはまだ生きているので、ThreadStaticプロパティオブジェクトもまだ生きています。私は少しでは何も表示されていない」:私はしていない、特に明示的に各ループの繰り返し

    ピーターDunihoの終わりにnullにそれらを設定するアイデアのように右のそれを持っている

    あなたが投稿したコードは[ThreadStatic]ThreadLocal<T>またはAsyncLocal<T>のいずれかの使用を正当化します。一時的なスレッドの場合、通常、適切なコンテキストオブジェクトをスレッドに渡すだけです...これらの他のすべてのメカニズムは、ここで必要と思われないセマンティクスを持っています。

    +2

    答えの中でスレッドの静的な "オブジェクト"を参照しますが、オブジェクトはスレッドの静的ではないため、変数は "オブジェクト"の使用のほとんどが "可変"である必要があります。 – Servy

    +0

    @Stephen Cleary:そうです。スレッドが自分のアプリケーション(このプロセスのスレッドプール)に関連付けられている限り、このデータは保持されます。ありがとう、それは私の質問に答えました。 PeterのDunihoのコメントに関しては、 "ProcessItem()"というメソッドがミニアプリケーションであると述べました。確かに、それはアーキテクチャがこのアプローチを必要とする既存のアプリケーションのライブラリを呼び出しています。残念ながら、私はそれを変更できません。 – DrGriff

    関連する問題