3

並列で最大 300 個のジョブを処理する必要があるジョブ プロセッサがあります (ジョブは完了するまでに最大 5 分かかる場合がありますが、通常はネットワークにバインドされています)。

私が抱えている問題は、仕事が特定の種類の塊になる傾向があるということです. 簡単にするために、 から までの 6 つのジョブ タイプがあるとJobAJobFます。

JobA-JobEネットワークにバインドされており、システムにまったく負担をかけずに 300 を一緒に実行することができます (実際、テストで 1,500 以上を並べて実行することができました)。JobF(新しいジョブ タイプ) もネットワークにバインドされていますが、かなりの量のメモリが必要であり、実際には GDI 機能を使用します。

すべての GDI オブジェクトをusings で慎重に破棄していることを確認しており、プロファイラーによると、何もリークしていません。単純に、300JobFを並列で実行すると、.NET が提供するよりも多くのメモリが使用されるということです。

これに対処するベストプラクティスの方法は何ですか? 私が最初に考えたのは、メモリのオーバーヘッドがどれくらいあるかを判断し、制限に近づくにつれて新しいジョブの生成を抑制することでした (少なくともJobFジョブ)。フレームワークがメモリの観点から私に割り当てようとしているものを確実に判断する方法が見つからないため、これを達成できませんでした。また、少し不安定に見えるジョブで使用される最大メモリを推測する必要があります。

私の次の計画は、OOM を取得した場合は単純に調整し、失敗したジョブを再スケジュールすることでした。残念ながら、OOM は問題のあるジョブ内だけでなく、どこでも発生する可能性があります。実際、最も一般的な場所は、ジョブを管理するメインのワーカー スレッドです。現状では、これによりプロセスが正常にシャットダウンし (可能であれば)、再起動して回復を試みます。これは機能しますが、厄介で時間とリソースの浪費です。その特定のジョブをリサイクルするよりもはるかに悪いことです。

この状況を処理する標準的な方法はありますか (メモリを追加することはオプションであり、実行されますが、アプリケーションは爆撃だけでなく、この状況を適切に処理する必要があります)。

4

4 に答える 4

2

単純に、300 個の JobF を並行して実行すると、.Net が提供するよりも多くのメモリが使用されるということです。

それでは、これをしないでください。システムのThreadPoolでジョブをキューに入れます。または、スケールアウトして負荷をより多くのシステムに分散します。

また、CERを調べて、メモリ不足の例外が発生した場合に少なくともクリーンアップ コードを実行してください。

更新:GDIを使用すると述べたので、注意すべきもう1つのことは、メモリ不足でないOutOfMemoryException条件をスローできることです。

于 2012-07-04T19:14:32.320 に答える
2

私はあなたのケースと同様のことをリモートで行っています.1つのタスクプロセッサ(1つのノードで実行されるメインキューマネージャー)と、1つ以上のノードで実行される多くのAGENTSを持つアプローチを選択しました.

各エージェントは、個別のプロセスとして実行されます。彼ら:

  • タスクの空き状況を確認する
  • 必要なデータをダウンロードする
  • プロセスデータ
  • アップロード結果

キュー マネージャーは、ジョブの実行中にいずれかのエージェントが失敗した場合、しばらくしてから別のエージェントに単純に再割り当てされるように設計されています。

1 つのボックスで並行して実行される 8 つのエージェント

ところで、すべてのタスクを一度に並行して実行しないことを検討してください。これは、コンテキストの切り替えで実際にいくらかのオーバーヘッドが発生するためです (かなりの量になる可能性があります)。あなたの場合、実際の DATA トラフィックではなく、不要な PROTOCOL トラフィックでネットワークを飽和させている可能性があります。

この設計のもう 1 つの優れた点は、データ処理が遅れ始めた場合、いつでももう 1 台のマシン (Amazon C2 インスタンスなど) をオンにして、タスクベースをより迅速に完了するのに役立つ複数のエージェントをさらに実行できることです。

あなたの質問への答えとして:

1 つのホストで実行されるエージェントの数には限りがあるため、すべてのホストは可能な限り多くのリソースを使用します。1 つのタスクが完了すると、別のタスクが実行され、無限に続きます。データベースは使いません。タスクはタイムクリティカルではないため、受信データセットを何度も繰り返し、以前の実行で何かが失敗した場合に新しいタスクを作成する 1 つのプロセスがあります。具体的には:

http://access3.streamsink.com/archive/ (ソースデータ)

http://access3.streamsink.com/tbstrips/(計算結果)

キュー マネージャーが実行されるたびに、ソースと宛先がスキャンされ、結果のセットが差し引かれ、ファイル名がタスクに変換されます。

そしてさらにいくつか:

私は Web サービスを使用してジョブ情報を取得し、結果を返し、単純な http を使用して処理用のデータを取得しています。

ついに:

これは、私が持っている 2 つのマネージャー/エージェントのペアのうち、より単純なものです。もう 1 つはやや複雑なので、ここでは詳しく説明しません。電子メールを使用してください:)

于 2012-07-05T00:53:08.547 に答える
1

理想的には、プロセス プロファイルに分割できます。CPU バウンド、メモリ バウンド、IO バウンド、ネットワーク バウンド。私は並列処理の新人ですが、TPL がうまく機能するのは CPU バウンドであり、実際には MaxDegreeOfParallelism をはるかに超えて調整することはできません。

開始は CPU バウンドです get MaxDegreeOfParallelism = System.Environment.ProcessorCount -1

そして、それ以外はすべて MaxDegreeOfParallelism = 100 になります。ネットワークの規模は大きくなるとおっしゃっていましたが、ある時点で限界は帯域幅です。300 個のジョブ (メモリを消費する) をスピンアップすると、本当にスループットが向上しますか? もしそうなら、Joradao からの答えを見てください。

于 2012-07-04T23:09:32.283 に答える
0

オブジェクトが IDisposable インターフェイスを実装している場合、メモリ リークが発生する可能性があるため、Garbage Recollector に依存しないでください。

たとえば、そのクラスがある場合:

class Mamerto : IDisposable
{
    public void methodA()
    {
        // do something
    }

    public void methodB()
    {
        // do something
    }

    public void Dispose()
    {
        // release resources
    }

そして、そのクラスを次のように使用します。

using( var m = new Mamerto() )
{
    m.methodA();
    m.methodB();
    // you should call dispose here!
}

ガベージ リコレクターは m オブジェクトを「削除準備完了」としてマークし、Gen 0 コレクションに配置します。ガベージ リコレクターが Gen 0 のすべてのオブジェクトを削除しようとすると、Dispose メソッドを検出し、オブジェクトを Gen 1 に自動的に昇格させます (そのオブジェクトを削除するのは「それほど簡単ではないため」)。Gen 1 オブジェクトは Gen 0 オブジェクトほど頻繁にチェックされないため、メモリ リークが発生する可能性があります。

詳細については、その記事をお読みくださいhttp://msdn.microsoft.com/en-us/magazine/bb985011.aspx

明示的な Dispose を続行すると、その厄介なリークを回避できます。

using( var m = new Mamerto() )
{
    m.methodA();
    m.methodB();
    m.Dispose();
}
于 2012-07-04T16:55:19.700 に答える