2

私の C#/.NET 3.5 プログラムでは、Threadpool スレッド (デリゲート + BeginInvoke/EndInvoke) を使用して、一部のファイルの読み込みを並列化し、高速化しています。SystemInternals ツールの ProcessExplorer は、処理中のスレッドの数が時間の経過とともに増加していることを示していますが、私は同じままであると予想しています。一部の Threads/Threads ハンドルが理由もなくぶら下がっているようです。

興味深いことに、アプリケーションを起動するたびに繰り返し可能なパターンがなく、スレッドが成長し、散発的に発生するように見えるパターンを見つけることができません。私は分析に時間を費やしており、ここにいくつかの観察結果があります:

1) コードは次のようになります。

ArrayList IAsyncResult_s = new ArrayList();
   AsyncProcessing thread1 = processRasterLayer;
... ArrayList filesToRender....
 foreach (string FileName in filesToRender)
    {
      string fileName2 = FileName;
      GeoImage partialImage1;
      IAsyncResult asyncResult = thread1.BeginInvoke(
                                fileName2, .....,
                                out partialImage1, ..., null, null);
      IAsyncResult_s.Add(asyncResult);
      asyncResult = null;
    }
.................
//block and render all
foreach (IAsyncResult asyncResult in IAsyncResult_s)
{
  GeoImage partialImage1;
  thread1.EndInvoke(
  out partialImage1,   , asyncResult);
  //render image.. some calls to render partial image here
  partialImage1.Dispose();
  partialImage1 = null;
}
IAsyncResult_s.Clear();
IAsyncResult_s = null;
thread1 = null;

2) プロセス スレッドの数 私のトレースは、ループ内での実行中にThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);、493、1000 のような数値を与えることを示していThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);ます。 SystemInternals ProcessExplorerAPI System.Diagnostics.Process.GetCurrentProcess().Threads.Count は、ループの前に 16、ループの後に約 21 です。これらのループを再度呼び出すと、処理中のスレッドの数は増加しますが、毎回固定数ではなく、上記のコードを繰り返すたびに 1 ~ 4 増加するため、16->21->22->26->31 のように増加します。 ...

3) 強制ガベージ コレクションが実行されなかった 余分なスレッドを削除するためにガベージ コレクションを停止しようとしましたが、プロセスから削除されませんでした。

4) プロフリング ツール RedGates のメモリとパフォーマンスのプロファイラーを使用していましたが、明確な理由が見つかりませんでした。いくつかの余分なスレッドとそのオブジェクト (ThreadContext など) がハングしているのを見ましたが、それらのスレッドをメモリに保持しているオブジェクトは見られませんでした。呼び出し内にスレッド名を追加したため、これらの余分なスレッドが上記のループ作業に関与していたことは間違いありません。

5) Intelitrace Intelitrace のデバッグでは、余分なスレッドがハングしていることも示されました。彼らは私が付けた名前をまだ持っています。しかし興味深いことに、現在ハングしている同じスレッドが過去に上記のループで使用されていたことも示されましたが、同じスレッドが私のコードのタイマーからいくつかのタイマー関連のイベントを実行していたことも示されました。

6) 問題の特定 そのため、filse を非同期的に処理する上記のループを無効にし、ファイルを順次ロードすると、余分なスレッドはなく、アプリケーションのスレッド数は一定で、約 16 です。

7) SetMaxThreads について:私のマシンでどのように見えるか (XP、.NET 3.5):

次のようなコード:

ThreadPool.GetAvailableThreads(out AvailableWorkerThreads、out AvailableCompletionPortThreads);
ThreadPool.GetMaxThreads(アウト MaxWorkerThreads、アウト MaxCompletionPortThreads);
ThreadPool.GetMinThreads (MinWorkerThreads から、MinCompletionPortThreads から);

結果を与える:

MinWorkerThreads:2 MaxWorkerThreads:500 MinCompletionPortThreads:2 MaxCompletionPortThreads:1000 AvailableWorkerThreads:500 AvailableCompletionPortThreads:1000

私のアプリは、おそらく 8 つのワーカー スレッドを同時に使用しています。SetMaxThreads に問題はありません。

8) 機能的には、これまでのところ、上記のソリューションで問題はありません。しかし、どういうわけか、アプリ内のスレッド数が増加しているとツールが報告した場合、それはある種の「リソース リーク」のように見えるので、対処したいと思います。いくつかのスレッド ハンドルが理由もなくぶら下がっているようです。

9) ここに興味深い記事があります。EndInvoke が呼び出されると、スレッド リソースが消去されるのは当然のことです。私は自分のコードでそうしています。記事の意味: ..」. EndInvoke は生成されたスレッドの後にクリーンアップするため、BeginInvoke ごとに EndInvoke が呼び出されるようにする必要があります。」「スレッド プール スレッドが終了した場合、EndInvoke は次のことを行います。終了したスレッドのルーズ エンドをクリーンアップし、そのリソースを破棄します。」参照: http://en.csharp-online.net/Asynchronous_Programming%E2%80%94BeginInvoke_EndInvoke

10) 別の興味深い記事。著者は、非 GUI スレッドからコントロールを作成していたため、スレッド ハンドル リークが発生したと述べています。これはかなり精巧な記事です。http: //msmvps.com/blogs/senthil/archive/2008/05/29/the-case-of-the-leaking-thread-handles.aspxを参照してください。

11) もう一つの興味深い記事。ThreadPool.SetMinThreads プロパティについて説明します。ThreadPool.SetMaxThreads ではなく、ThreadPool.SetMinThreads が ThradPool の有用な制御を可能にしているようです。この記事は私にとって目を見張るものであり、ThreadPool がどのように機能するか、および ThreadPool が引き起こす可能性のあるパフォーマンスの問題について考えさせられました。記事は http_://www.dotnetperls.com/threadpool-setminthreads です。別の同様のものは次のとおりです。 http_://www.codeproject.com/Articles/3813/NET-s-ThreadPool-Class-Behind-The-Scenes

12) 別の興味深い記事。ThreadPool のスロットリングの問題について話しています。記事では、1 秒あたり 2 つの新しいスレッドの増加という ThreadPool の制限について言及しています。http_://social を参照してください。msdn. マイクロソフト。com/forums/en-US/clr/thread/3325cb32-371b-4f3e-965f-6ca88538dc3e/

13) したがって、おそらく 30 回のテストで、割り当てられたスレッド数が減少するのは 2 回だけでした。しかし、それは起こりました。スレッド番号が 16->....->31->61-> ->30->16 のようになるのを見たことがあります。それで、それは 16 に戻りました。それは頻繁に発生するわけではなく、待つ時間ではありません。進行中の大きなアクティビティの後に一定の低レベル アクティビティの期間が続くようなものでした。

14) ThreadPool.SetMinThreads メソッドのドキュメント。スレッドプールの 1 秒あたり 2 つの新しいスレッドの制限について説明しています。このプロパティを設定するとその制限がなくなるかどうかは不明です。http_://msdn.microsoft. com/en-ca/library/system. threading.threadpool.setminthreads(v=vs.90).aspx

4

2 に答える 2

3

答えは次のとおりです。ここには漏れはありません。これがスレッドプールの仕組みです。作業を終了したスレッドを保持するため、次にスレッドが必要になったときにスレッド作成の代価を支払う必要がありません。同時作業項目が多数ある場合、プール内のスレッド数は増加しますが、MaxWorkerThreads で最大になります。(ガベージコレクタとは関係ありません。)

詳細については、この記事を参照してください: http://msdn.microsoft.com/en-us/library/0ka9477y.aspx

于 2013-03-17T09:36:37.530 に答える
0

私は消費者生産者パターンを検討します。スレッドプールの背後にある考え方は、何百もの新しいスレッドを作成するのではなく、スレッドをリサイクルすることです。最良のケースでは、CPU ごとに 1 つのスレッドがあり、作業をキューに入れます。無駄なコンテキストスイッチを避け、新しいスレッドの作成を待つので、これは確実に高速になります.netスレッドプールは、新しいスレッドが作成されるまで約1秒待機して、他のスレッドがリサイクルされる機会を与えることを覚えています.

于 2013-03-22T06:32:30.780 に答える