8

これがこれを行うための最良の方法かどうか疑問に思っています。無期限に実行される約 500 のスレッドがありますが、1 サイクルの処理が完了すると Thread.sleep が 1 分間発生します。

   ExecutorService es = Executors.newFixedThreadPool(list.size()+1);
   for (int i = 0; i < list.size(); i++) {
      es.execute(coreAppVector.elementAt(i)); //coreAppVector is a vector of extends thread objects
   }

実行中のコードは非常に単純で、基本的にはこれだけです

class aThread extends Thread {
   public void run(){
      while(true){
         Thread.sleep(ONE_MINUTE);
         //Lots of computation every minute
      }
   }
}

実行中のタスクごとに個別のスレッドが必要なので、アーキテクチャを変更することはできません。threadPool のサイズを Runtime.getRuntime().availableProcessors() に等しくしようとしましたが、これは 500 個のスレッドすべてを実行しようとしましたが、そのうちの 8 個 (4xhyperthreading) しか実行できませんでした。他のスレッドは降伏せず、他のスレッドに順番を任せます。私はwait()とnotify()を入れてみましたが、まだうまくいきません。誰かが簡単な例やヒントを持っていれば、私は感謝しています!

うーん、デザインに欠陥があるのは間違いない。スレッドは、学習アルゴリズムの一種である遺伝的プログラミングまたは GP を実装します。各スレッドは、高度な傾向を分析して予測を行います。スレッドが完了すると、学習は失われます。そうは言っても、sleep() を使用すると、1 つのスレッドが「学習」していないときにリソースの一部を共有できるようになることを望んでいました。

したがって、実際の要件は

状態を維持して 2 分ごとに実行するタスクをスケジュールし、同時に実行するタスクの数を制御するにはどうすればよいですか。

4

11 に答える 11

13

スレッドが終了しない場合、これはスレッド プールではなく、スレッド内のコードに問題があります。詳細なヘルプについては、実行中のコードを投稿する必要があります。

また、完了時に各スレッドをスリープ状態にするのはなぜですか。完成させたほうがいいのではないですか?

さらに、実行したいタスクの数と同じ数のスレッドを持つことで、スレッドプールを誤用していると思います。スレッド プールのポイントは、使用されるリソースの数に制約を課すことです。このアプローチは、スレッド プールをまったく使用しないことに勝るものはありません。

最後に、 のインスタンスだけThreadをに渡す必要はありません。 無限にループするスレッドの独自のプールを維持し、内部キューから作業を引き出します (作業は送信した s です)。ExecutorServiceRunnableExecutorServiceRunnable

于 2010-05-19T18:28:35.393 に答える
10

これらすべてのスレッドを丸 1 分間アイドル状態のままにしておくのではなく、 を使用して、各タスクを 1 分に 1 回実行するScheduledExecutorServiceようにスケジュールしてみませんか?

ScheduledExecutorService workers = 
  Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
for (Runnable task : list) { 
  workers.scheduleWithFixedDelay(task, 0, 1, TimeUnit.MINUTES);
}

「アーキテクチャの変更はオプションではない」とはどういう意味ですか? タスクをまったく変更できないことを意味する場合(具体的には、タスクを 1 回実行する代わりにループする必要があり、への呼び出しをThread.sleep()削除することはできません)、「優れたパフォーマンスはオプションではありません」 .

于 2010-05-19T18:59:46.610 に答える
3

あなたのコードがスレッドプールをどのように使用しているかについて、意味的に正しいかどうかはわかりません。ExecutionService は内部でスレッドを作成および管理します。クライアントは Runnable のインスタンスを提供するだけでよく、その run() メソッドはプールされたスレッドの 1 つのコンテキストで実行されます。私の例を確認できます。また、実行中の各スレッドはスタック用に最大 10Mb のシステム メモリを使用し、Linux では Java からネイティブ スレッドへのマッピングは 1 対 1 であることにも注意してください。

于 2010-05-19T18:37:53.277 に答える
2

トレッドをスリープ状態にする代わりに、トレッドを戻して、ThreadPoolexecutorを使用して、作業キューに毎分ポストされた作業を実行する必要があります。

于 2010-05-19T18:39:30.120 に答える
2

あなたの質問に答えるために、スレッドプールのタイプは何ですか?

コメントを投稿しましたが、これは本当にあなたの問題に対処する必要があります。完了するのに 2 秒かかる計算があります。できるだけ早く完了したい多くのタスク (500) があります。IO やネットワーク トラフィックがないと仮定すると、実現可能な最速のスループットはRuntime.getRuntime().availableProcessors()、スレッドの数によって決まります。

数を 500 スレッドに増やすと、各タスクは独自のスレッドで実行されますが、OS は頻繁にスレッドをスケジュールして別のスレッドに渡します。これは、任意の時点で 125 のコンテキスト スイッチです。コンテキストを切り替えるたびに、各タスクの実行時間が長くなります。

ここでの全体像は、プロセッサの数をはるかに超えている場合、スレッドを追加してもスループットが向上するわけではないということです。

編集:簡単な更新。ここで寝る必要はありません。8 つのプロセッサで 500 のタスクを実行すると、各タスクは 2 秒で完了し、終了すると、そのタスクが実行されていたスレッドが次のタスクを実行してそのタスクを完了します。

于 2010-05-19T18:52:30.200 に答える
1

これはあなたが望むことをするはずですが、あなたが求めたものではありません:-)Thread.sleep()

ScheduledRunnable.java

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledRunnable
{
    public static void main(final String[] args)
    {
        final int numTasks = 10;
        final ScheduledExecutorService ses = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
        for (int i = 0; i < numTasks; i++)
        {
            ses.scheduleAtFixedRate(new MyRunnable(i), 0, 10, TimeUnit.SECONDS);
        }
    }

    private static class MyRunnable implements Runnable
    {
        private int id;
        private int numRuns;

        private MyRunnable(final int id)
        {
            this.id = id;
            this.numRuns = 0;
        }

        @Override
        public void run()
        {
            this.numRuns += 1;
            System.out.format("%d - %d\n", this.id, this.numRuns);
        }
    }
}

Runnablesこれにより、10 秒ごとに動作が表示されるようにスケジュールされます。処理が完了してから一定時間待機する必要がある場合は、必要.scheduleXXXなメソッドをいじる必要があるかもしれません。実行時間に関係なく、fixedWait は N 時間ごとに実行されると思います。

于 2010-05-19T20:34:27.703 に答える
1

システムが処理できる最大スレッド数は 8 スレッドであり、それ以上ではコンテキスト切り替えで速度が低下します。

この記事を見てください http://www.informit.com/articles/article.aspx?p=1339471&seqNum=4それを行う方法の概要を説明します。

于 2010-05-19T18:50:37.543 に答える
0

Akkaのようなエージェントベースの同時実行フレームワークを使用するようにプロジェクトを書き直すことはできますか?

于 2010-05-19T20:02:13.183 に答える
0

実行中のタスクごとに個別のスレッドが必要なので、アーキテクチャを変更することはできません。

それが当てはまる場合(たとえば、外部ブロッキング関数を呼び出す場合)、それらに対して個別のスレッドを作成し、それらを開始します。スレッドの 1 つのブロッキング機能により、他の実行可能ファイルがそこに入れられなくなり、タスクごとに 1 つのスレッドでスレッド プールを作成してもあまり効果がないため、限られた数のスレッドでスレッド プールを作成することはできません。

threadPool のサイズを Runtime.getRuntime().availableProcessors() に等しくしようとしましたが、これは 500 個のスレッドすべてを実行しようとしましたが、そのうちの 8 個 (4xhyperthreading) しか実行できませんでした。

作成している Thread オブジェクトをスレッド プールに渡すと、実装されていることがわかりますRunnable。したがって、それぞれRunnableが完了するまで実行されます。run()メソッドの戻りを停止するループは、キューに入れられた次のタスクの実行を許可しません。例えば:

public static void main (String...args) {
    ExecutorService executor = Executors.newFixedThreadPool(2);

    for (int i = 0; i < 10; ++i) {
        final int task = i;

        executor.execute(new Runnable () {
        private long lastRunTime = 0;
            @Override
            public void run () {

                for (int iteration = 0; iteration < 4; )
                {
                    if (System.currentTimeMillis() - this.lastRunTime > TIME_OUT)
                    {
                        // do your work here
                        ++iteration;
                        System.out.printf("Task {%d} iteration {%d} thread {%s}.\n", task, iteration, Thread.currentThread());

                        this.lastRunTime = System.currentTimeMillis();
                    }
                    else
                    {
                        Thread.yield(); // otherwise, let other threads run
                    }
                }
            }
        });
    }

    executor.shutdown();
}

プリントアウト:

Task {0} iteration {1} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {1} thread {Thread[pool-1-thread-2,5,main]}.
Task {0} iteration {2} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {2} thread {Thread[pool-1-thread-2,5,main]}.
Task {0} iteration {3} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {3} thread {Thread[pool-1-thread-2,5,main]}.
Task {0} iteration {4} thread {Thread[pool-1-thread-1,5,main]}.
Task {2} iteration {1} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {4} thread {Thread[pool-1-thread-2,5,main]}.
Task {3} iteration {1} thread {Thread[pool-1-thread-2,5,main]}.
Task {2} iteration {2} thread {Thread[pool-1-thread-1,5,main]}.
Task {3} iteration {2} thread {Thread[pool-1-thread-2,5,main]}.
Task {2} iteration {3} thread {Thread[pool-1-thread-1,5,main]}.
Task {3} iteration {3} thread {Thread[pool-1-thread-2,5,main]}.
Task {2} iteration {4} thread {Thread[pool-1-thread-1,5,main]}.
...

次のタスクがスケジュールされる前に、最初の (スレッド プール サイズ) タスクが完了するまで実行されることを示しています。

あなたがする必要があるのは、しばらく実行するタスクを作成してから、他のタスクを実行させることです。これらをどのように構築するかは、何を達成したいかによって異なります

  • すべてのタスクを同時に実行するか、すべてのタスクを 1 分間待ってからすべて同時に実行するか、またはタスクを互いに同期させないかどうか
  • 各タスクを 1 分間隔で実行することが本当に必要かどうか
  • タスクが潜在的にブロックされているかどうか、したがって実際には別のスレッドが必要かどうか
  • 予想される実行ウィンドウよりも長くタスクがブロックされた場合に予想される動作
  • タスクが繰り返し率よりも長くブロックされた場合 (1 分以上ブロックされた場合)、どのような動作が予想されるか

これらに対する回答に応じて、ScheduledExecutorService、セマフォ、またはミューテックスの組み合わせを使用して、タスクを調整できます。最も単純なケースは、ノンブロッキングで非同期のタスクです。この場合、ScheduledExecutorService を直接使用して、ランナブルを毎分 1 回実行します。

于 2010-05-19T19:41:42.127 に答える
-1

システムが実際に処理できる数までスレッドの数を減らすことで、スループットがいくらか向上することは確かです。スレッドのデザインを少し変更してもよろしいですか?実際に何百ものスリープ状態のスレッドを持つ代わりに、スリープ状態のスレッドをキューに入れるためにスケジューラーの負担を軽減します。

class RepeatingWorker implements Runnable {

private ExecutorService executor;
private Date lastRan;

//constructor takes your executor

@Override
public void run() {

  try {
    if (now > lastRan + ONE_MINUTE) {
      //do job
      lastRan = now;
    } else {
      return;
  } finally {
    executor.submit(this);
  }
}
}

これにより、「ジョブは無期限に繰り返されますが、実行間で少なくとも 1 分間待機します」というコア セマンティックが保持されますが、マシンが処理できるものにスレッド プールを調整し、動作していないものはうろつくのではなくキューに入れられます。スリープ スレッドとしてスケジューラで。誰も実際に何もしていない場合、いくつかの待機ビジー動作がありますが、あなたの投稿から、アプリケーションの全体的な目的はこれらのスレッドを実行することであり、現在プロセッサをレールに乗せていると推測しています。他のもののために余地を作る必要がある場合は、その周りを調整する必要があるかもしれません:)

于 2010-05-19T19:16:32.133 に答える
-1

セマフォが必要です。

class AThread extends Thread {
   Semaphore sem;
   AThread(Semaphore sem) {
     this.sem = sem;
   }
   public void run(){
      while(true){
         Thread.sleep(ONE_MINUTE);
         sem.acquire();
         try {
           //Lots of computation every minute
         } finally {
           sem.release();
         }
      }
   }
}

AThreads をインスタンス化するときは、同じセマフォ インスタンスを渡す必要があります。

Semaphore sem = new Semaphore(MAX_AVAILABLE, true);

編集: 反対票を投じたのは誰ですか?理由を説明してください。私の解決策に何か問題がありますか?

于 2010-05-19T18:45:19.397 に答える