マルチスレッド化(つまり、スレッドプールベース)は、Javaアプリケーション内の破損結果

クリスチャンPagot:

私は、より具体的には、Javaでスレッドプールをマルチスレッドで実験しています。テストとして、私は単に速度のためにマルチスレッド使用して画像の色を変更するアプリケーションを書きました。しかし、私にはいくつかの理由は不明のため、私は、このテストの設定方法に応じて、破損した結果が得られます。下に私は、テストアプリケーションは、完全なソースコードと一緒にどのように動作するかを説明します。

すべてのヘルプは大歓迎です!ありがとうございました!

テストアプリケーション

私は以下のように、濃い青色で初期化された400×300画素の画像バッファを有しています。

ここでは、画像の説明を入力します。

プログラムは、赤い色でそれを完全に埋める必要があります。

私は単純にすべてのピクセルを超えるループは、赤で順次各1を着色、私が決めたことができますが、パフォーマンスのために、並列処理を利用することができます。したがって、私は別のスレッドで各画像列を埋めることにしました。行(300行)の数は、利用可能なCPUコアの数はるかに大きいので、私は300回の作業(一列充填担当それぞれ)を消費する(4つのスレッドを含む)スレッドプールを作成しました。

次のようにプログラムが構成されています。

  • RGBクラス:ダブルスの3組の画素の色を保持しています。
  • RenderTaskクラス:赤色を有する画像バッファの所与の行がいっぱい。
  • レンダラークラス:
    • 画像バッファを作成します。
    • 「newFixedThreadPool」とのスレッドプールを作成します。
    • スレッドプールで消費される300個のタスクを作成します。
    • スレッドプールのサービスを終了。
    • 画像バッファは、PPMファイルに書き込みます。

以下は、完全なソースコードを見つけることができます(私はこのコードを呼び出しますバージョン1):

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.io.*;

class RGB {
    RGB() {}

    RGB(double r, double g, double b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }

    double r;
    double g;
    double b;
}

class RenderTask implements Runnable {
    RenderTask(RGB[][] image_buffer, int row_width, int current_row) {
        this.image_buffer = image_buffer;       
        this.row_width = row_width;
        this.current_row = current_row; 
    }

    @Override
    public void run() {   
        for(int column = 0; column < row_width; ++column) {
            image_buffer[current_row][column] =  new RGB(1.0, 0.0, 0.0);
        }
    }

    RGB[][] image_buffer;
    int row_width;
    int current_row;
}

public class Renderer {
    public static void main(String[] str) {
        int image_width = 400;
        int image_height = 300;

        // Creates a 400x300 pixel image buffer, where each pixel is RGB triple of doubles,
        // and initializes the image buffer with a dark blue color.
        RGB[][] image_buffer = new RGB[image_height][image_width];
        for(int row = 0; row < image_height; ++row)
            for(int column = 0; column < image_width; ++column)
                image_buffer[row][column] = new RGB(0.0, 0.0, 0.2); // dark blue        

        // Creates a threadpool containing four threads
        ExecutorService executor_service = Executors.newFixedThreadPool(4);

        // Creates 300 tasks to be consumed by the threadpool:
        //     Each task will be in charge of filling one line of the image buffer.
        for(int row = 0; row < image_height; ++row)
            executor_service.submit(new RenderTask(image_buffer, image_width, row));

        executor_service.shutdown();

        // Saves the image buffer to a PPM file in ASCII format
        try (FileWriter fwriter = new FileWriter("image.ppm");
            BufferedWriter bwriter = new BufferedWriter(fwriter)) {

            bwriter.write("P3\n" + image_width + " " + image_height + "\n" + 255 + "\n");

            for(int row = 0; row < image_height; ++row)
                for(int column = 0; column < image_width; ++column) {
                    int r = (int) (image_buffer[row][column].r * 255.0);
                    int g = (int) (image_buffer[row][column].g * 255.0);
                    int b = (int) (image_buffer[row][column].b * 255.0);
                    bwriter.write(r + " " + g + " " + b + " ");
                }                
        } catch (IOException e) {
            System.err.format("IOException: %s%n", e);
        }
    }
}

すべてはそのコードで動作しているようだ、と以下のように私は、予想される赤画像バッファを取得します:

ここでは、画像の説明を入力します。

問題

私はRenderTask.run()メソッドを変更した場合しかし、それは再設定するように重複以下のように同じバッファ位置の色順序で複数回、(私はこの1つ呼ぶバージョン2):

    @Override
    public void run() {   
        for(int column = 0; column < row_width; ++column) {
            for(int s = 0; s < 256; ++s) {

                image_buffer[current_row][column] =  new RGB(1.0, 0.0, 0.0);

            }
        }
    }

それから私は、次の壊れた画像バッファを取得します:

ここでは、画像の説明を入力します。

実際には、結果は私がプログラムを実行するたびに異なりますが、常に壊れ。

私はそれを理解する限りとして、見えないレースの条件が存在しないようですので、何の2つのスレッドが、同時に同じメモリ位置への書き込みはありません。

Even in the case of "false sharing", which I don't think is happening, I would expect only lower performance, not corrupted results.

Thus, even with redundant assignments, I would expect to get the correct result (i.e. a completely red image buffer).

So, my questions are: Why is this happening to the Version 2 of the program if the only difference with respect to Version 1 is that the assignment operation is being executed redundantly within the scope of the thread?

Would it be the case that some threads are being destroyed before they finish? Would it be a bug in the JVM? Or have I missed something trivial? (the strongest hypothesis :)

Thank you guys!!

emil :

ExecutorService.shutdown() does not await termination of the tasks it has, it only stops accepting new tasks.

あなたがシャットダウンを呼び出した後、あなたはそれが完了するまで待機したい場合は、エグゼキュータのサービスにawaitTerminationを呼び出す必要があります。

だから何が起こっていることは、あなたがファイルにイメージを書き始めていたときにすべてのタスクがまだ実行を終了していないということです。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=312657&siteId=1