2

以下のコードでは、スレッドプールを介して関数を何度も呼び出しています。この関数内で、FastestMemory と呼ばれるグローバル属性を介して、関数の最速実行を追跡します。

ただし、スレッドプール ループの後に値を出力すると、グローバル変数がループの反復ごとに更新される変数と同じではないかのように、元の値が与えられます。FastestMemory の値が (たとえば) 253475 に割り当てられることがわかっている場合、2000000000 が返されます。

1) 最速の反復を追跡できるようにするために、このコードを再構成する必要がありますか?

2) このコードを非常に高速に実行できるようです。4 つの Xeon x7550 で反復ごとに (平均して) 1 ミリ秒未満かかります。これは正常ですか、それとも私のタイミングがどこか間違っていますか? C# は平均で約 400 ミリ秒かかる?!?

public class PoolDemo {

    static long FastestMemory = 2000000000;
    static long SlowestMemory = 0;
    static long TotalTime;
    static long[] FileArray;
    static DataOutputStream outs;
    static FileOutputStream fout;


  public static void main(String[] args) throws InterruptedException, FileNotFoundException {

        int Iterations = Integer.parseInt(args[0]);
        int ThreadSize = Integer.parseInt(args[1]);

        FileArray = new long[Iterations];
        fout = new FileOutputStream("server_testing.csv");

        // fixed pool, unlimited queue
        ExecutorService service = Executors.newFixedThreadPool(ThreadSize);
        ThreadPoolExecutor executor = (ThreadPoolExecutor) service;

        for(int i = 0; i<Iterations; i++) {
          Task t = new Task(i);
          executor.execute(t);
        }

        executor.shutdown();
        service.shutdown();

        System.out.println("Fastest: " + FastestMemory);

        for(int j=0; j<FileArray.length; j++){
            new PrintStream(fout).println(FileArray[j] + ",");
        }
      }

  private static class Task implements Runnable {

        private int ID;

        static Byte myByte = 0;

        public Task(int index) {
          this.ID = index;
        }

        @Override
        public void run() {
            long Start = System.nanoTime();

          int Size1 = 100000;
            int Size2 = 2 * Size1;
            int Size3 = Size1;

            byte[] list1 = new byte[Size1];
            byte[] list2 = new byte[Size2];
            byte[] list3 = new byte[Size3];

            for(int i=0; i<Size1; i++){
                list1[i] = myByte;
            }

            for (int i = 0; i < Size2; i=i+2)
            {
                list2[i] = myByte;
            }

            for (int i = 0; i < Size3; i++)
            {
                byte temp = list1[i];
                byte temp2 = list2[i];
                list3[i] = temp;
                list2[i] = temp;
                list1[i] = temp2;
            }

            long Finish = System.nanoTime();
            long Duration = Finish - Start;
            FileArray[this.ID] = Duration;
            TotalTime += Duration;
            System.out.println("Individual Time " + this.ID + " \t: " + (Duration) + " nanoseconds");


            if(Duration < FastestMemory){
                FastestMemory = Duration;
            }
            if (Duration > SlowestMemory)
            {
                SlowestMemory = Duration;
            }
        }
      }
}
4

3 に答える 3

5

問題は、エグゼキューターに送信されたタスクが終了するのを待たずにメインスレッドが終了していることです。

への呼び出しの後にExecutorService#awaitTerminationへの呼び出しを追加するだけで実現できますExecutorService#shutdown

static longもう 1 つの問題は、最速時間を追跡する値のスレッド セーフを考慮していないことです。安全な Compare-And-Set 操作を行うにsynchronizeは、ブロックを追加するか、 を使用する必要があります。AtomicLong

于 2012-04-04T16:55:17.970 に答える
1

shutdown()プールに送信された新しいタスクのみを拒否し、すでに送信され実行中のタスクには何もしません。すべてのタスクが完了するのを待つには、次の電話をかける必要があります。

executor.awaitTermination(1, TimeUnit.MINUTES);
service.awaitTermination(1, TimeUnit.MINUTES);

また、アクセスしFastestMemorySlowestMemory何らかの方法で同期する必要があります。次に例を示します。

synchronized(PoolDemo.class) {
    FastestMemory = Math.min(FastestMemory, Duration);
    SlowestMemory = Math.max(SlowestMemory, Duration);
}

ところで、Javaの命名規則によると:

すべてのインスタンス、クラス、およびクラス定数は、小文字の最初の文字と大文字と小文字が混在しています。

于 2012-04-04T16:56:17.540 に答える
0

まず、タスクが完了するのを待たないでください。Executor サービスが完了するまで待つ必要があります。

executor.awaitTermination(...);

また、エグゼキューターとサービスはどちらも同じオブジェクトを参照するため、両方をシャットダウンする必要はありません。実際、エグゼキューターさえも持つ理由はありません。呼び出すメソッドはすべて ExecutorService の一部です。

次に、長い変数への変更はスレッドセーフではありません。比較後に別のスレッドが値を変更するとどうなりますか。読み取りと書き込みでさえ、長い変数ではアトミックではありません。これらの変数には AtomicLongs を使用する必要があります。

AtomicLong から現在の値を取得し、現在の値の実行と比較します。新しい値が更新が必要であることを示している場合は、compareAndSet を使用して、他の誰も値を変更していないことを確認します。他の人が変更した場合は、チェックを再度実行します。

于 2012-04-04T16:59:26.190 に答える