4

Javaでスレッドプールを実装したいと思います。これは、送信されたタスクの計算およびI/O動作に基づいて動的にサイズを変更できます。

実際には、C#4.0の新しいスレッドプールの実装と同じ動作を実現したいと思います。

すでに実装されていますか、それともほとんど既存の同時実行ユーティリティ(CachedThreadPoolなど)を使用してこの動作を実現できますか?

C#バージョンは、最適な使用率を実現するために自己計測を行います。Javaで利用できるセルフインストルメンテーションと、現在のパフォーマンスへの影響は何ですか?

タスクがその意図を示す協調的なアプローチを実行することは可能ですか(たとえば、I / O集中型の操作に入る、CPU集中型の操作フェーズに入る)?

どんな提案でも大歓迎です。

コメントに基づいて編集:

ターゲットシナリオは次のとおりです。

  • ローカルファイルのクロールと処理
  • Webクロール
  • マルチWebサービスへのアクセスと集約

CachedThreadPoolの問題は、既存のすべてのスレッドがブロックされたときに新しいスレッドを開始することです。明示的な境界を設定する必要がありますが、それだけです。

たとえば、100個のWebサービスを連続してアクセスできます。100 CTPを作成すると、100スレッドが開始されて操作が実行され、大量の複数のI/O要求とデータ転送が確実にお互いにつまずきます。静的テストケースの場合、最適なプールサイズを実験して見つけることができますが、ある方法で適応的に決定して適用する必要があります。

4

4 に答える 4

2

キーがボトルネックリソースであるマップを作成することを検討してください。

プールに送信される各スレッドは、ボトルネックであるリソース、つまり「CPU」、「ネットワーク」、「C:」などを送信します。

リソースごとに1つのスレッドのみを許可することから始めて、作業完了率の増加が止まるまでゆっくりと増加する可能性があります。CPUのようなものは、コア数の下限を持つ可能性があります。

于 2009-07-27T20:32:11.187 に答える
1

別のアプローチを紹介します。シングルスレッドプールを持つことは素晴らしい抽象化ですが、特にジョブが非常にIOバウンドである場合、パフォーマンスはあまり高くありません-それを調整する良い方法はありません、IOスループットを最大化するためにプールサイズを爆破するのは魅力的ですが、あなたは苦しんでいますスレッドスイッチが多すぎるなどから。

代わりに、インスピレーションを得るためにApacheMINAフレームワークのアーキテクチャを検討することをお勧めします。(http://mina.apache.org/)これは高性能のWebフレームワークです。サーバーフレームワークとして説明されていますが、スパイダーやマルチサーバークライアントなどの逆のシナリオでもアーキテクチャがうまく機能すると思います。(実際には、プロジェクトですぐに使用できる場合もあります。)

これらは、すべてのIO操作にJava NIO(ノンブロッキングI / O)ライブラリを使用し、作業を2つのスレッドプールに分割します。小さくて高速なソケットスレッドのセットと、大きくて低速なビジネスロジックスレッドのセットです。したがって、レイヤーは次のようになります。

  • ネットワーク側では、それぞれにメッセージバッファを備えたNIOチャネルの大規模なセット
  • チャネルリストラウンドロビンを通過するソケットスレッドの小さなプール。彼らの唯一の仕事は、ソケットをチェックし、データをメッセージバッファに移動することです。メッセージが完了したら、メッセージを閉じてジョブキューに転送します。これらの人は、ビットをプッシュするだけで、IOでブロックされているソケットをスキップするため、高速です。
  • すべてのメッセージをシリアル化する単一のジョブキュー
  • キューからメッセージをプルし、それらを解析し、必要な処理を実行する処理スレッドの大規模なプール。

これにより、パフォーマンスが非常に向上します。IOは独自のレイヤーに分離され、ソケットスレッドプールを調整してIOスループットを最大化し、処理スレッドプールを個別に調整してCPU/リソースの使用率を制御できます。

于 2009-07-30T16:51:43.433 に答える
1

与えられた例は

Result[] a = new Result[N];
for(int i=0;i<N;i++) {
    a[i] = compute(i);
}

Javaでは、これをすべてのフリーコアに並列化し、作業負荷を動的に分散させる方法であるため、あるタスクに別のタスクより時間がかかるかどうかは関係ありません。

// defined earlier
int procs = Runtime.getRuntime().availableProcessors();
ExecutorService service = Executors.newFixedThreadPool(proc);

// main loop.
Future<Result>[] f = new Future<Result>[N];
for(int i = 0; i < N; i++) {
    final int i2 = i;
    a[i] = service.submit(new Callable<Result>() {
        public Result call() {
            return compute(i2);
        }
    }
}
Result[] a = new Result[N];
for(int i = 0; i < N; i++) 
    a[i] = f[i].get();

これは過去5年間あまり変わっていないので、最初に利用可能になったときほどクールではありません。Javaに本当に欠けているのはクロージャです。それが本当に問題である場合は、代わりにGroovyを使用できます。

追加:例としてではなく、パフォーマンスを重視する場合は、フィボナッチを並列で計算します。これは、シングルスレッドで計算すると高速になる関数の良い例だからです。

1つの違いは、各スレッドプールにはキューが1つしかないため、作業を盗む必要がないことです。これは、タスクあたりのオーバーヘッドが増えることを意味する可能性があります。ただし、タスクに通常約10マイクロ秒以上かかる限り、問題はありません。

于 2009-07-19T19:28:20.580 に答える
0

プラットフォーム固有の方法でCPU使用率を監視する必要があると思います。使用しているCPU/コアの数を確認し、負荷を監視します。負荷が低く、まだ作業が残っている場合は、新しいスレッドを作成します。ただし、num-cpusのx倍以下(たとえば、x = 2)にします。

IOスレッドも本当に検討したい場合は、プールが使い果たされたときに各スレッドがどのような状態にあるかを調べて、待機中のすべてのスレッドを総数から差し引いてください。ただし、1つのリスクは、あまりにも多くのタスクを許可することによってメモリを使い果たすことです。

于 2009-07-19T17:48:25.477 に答える