2

編集:解決しました、私の解決策を以下で見てください。

まず、これが私の最初の質問ですので、間違えた場合は教えてください。

トレーニング目的で、Javaでマンデルブロフラクタルプログラムを作成しようとしています。私が欲しいすべての機能にとって理想的なのはFractalizer(http://www.fractalizer.de/en/)ですが、今のところ、(代わりに)画面にマンデルブロ集合を描画するプログラムに満足しています。 、たとえば、画像ファイルへの書き込み)。もちろん、プログラムを高速にしたいので、計算を複数のスレッドに分割して、マルチコアプロセッサを利用できると思いました。たとえば、クアッドコアシステムでは、画像は2x2 = 4の画像に分割され、それぞれが個別のスレッドによって計算されます。これらのスレッドはすべて、計算時にピクセルを描画するGraphicsオブジェクトを取得します。

これを機能させるための最初の試みは、スレッドをBufferedImage.getGraphics()に描画させ、画像が完成していない限り、paint()メソッドが常にrepaint()を呼び出すようにすることでした。

g.drawImage(tempImg, 0, 0, null);
if (waiterThread.isAlive())
{
    try
    {
        Thread.sleep(10);
    } catch (InterruptedException e)
    {
        // do nothing
    }
    repaint(10);
}

(waiterThreadは、すべての計算スレッドを次々に結合します。waiterThreadが有効である限り、少なくとも1つの計算スレッドはまだ終了していません。)

これは機能しますが、頻繁に再描画されるため、キャンバスに醜いちらつきが発生します。

次に、小さなテストプログラムを使用して、Graphics.draw * anything *が、paintメソッドが戻る前に画面に即座に描画されることを発見したので、現在のアプローチは次のとおりです。

  • 2x2(<4コアシステムでは1x1)のMandelbrotCanvasオブジェクトを含むGridLayoutを備えた1つのパネル
  • 各MandelbrotCanvasオブジェクトは、最初のpaint()呼び出しで、計算スレッドを初期化し、独自のGraphicsオブジェクトを渡します(実際には、1つのGraphics呼び出しを複数のグラフィックに渡すカスタムGroupGraphicsクラスを使用して、画像をBufferedImage.getGraphics()に入れますが、これは重要ではありません)、計算スレッドを開始します。
  • パネルは、paint()メソッドで、各MandelbrotCanvasesから計算スレッドをフェッチし、それらをjoin()します。

残念ながら、これは黒い画面しか作成しません。計算が終了した場合のみ、画像が表示されます。

複数のスレッドを1つのコンポーネントにペイントする正しい方法は何ですか?

編集:

私が知らなかったこと:イベントディスパッチスレッドのみがAWTコンポーネントにペイントすることを許可されています(大まかに話されています)。つまり、上記の最後のアプローチはおそらく機能しない可能性があります-明らかに、例外をスローすることになっていますが、私はしませんでした1つ取得します。私の解決策は、最初のアプローチを使用することです-画像をBufferedImageに描画し、それをCanvasに描画します-唯一の変更は、update()メソッドをオーバーロードして、ペイント領域をクリアせずにpaint()メソッドを呼び出すことです。

public void update(Graphics g)
{
    paint(g);
}

したがって、一般的な質問(「複数のスレッドをAWTコンポーネントにペイントさせるにはどうすればよいですか?」)に対する私の答えは次のようになると思います。できません。許可されていません。スレッドがBufferedImage.getGraphics()に描画し、その画像を繰り返し描画します。ちらつきを避けるために、上記のようにupdate()メソッドをオーバーロードします。(今は本当に素晴らしいように見えます。)私の場合は使用できませんが、それでも良いもう1つのヒントは、再描画する必要のある領域を指定するために長方形の引数を取るrepaint()のバリアントがあることです。時間引数(ミリ秒単位)をとるバリアント。したがって、再描画をすぐに実行する必要はありません。

EDIT2:このリンクは非常に役立つ情報を提供します:http://java.sun.com/products/jfc/tsc/articles/painting/index.html

4

2 に答える 2

2

GUIスレッドのみがコンポーネントに直接ペイントできます。

したがって、repaintメソッドを呼び出す必要があります。

バックグラウンド計算がある場合、高速描画を強制するには、パラメータとして時間がかかるバージョンを使用する必要があります。

ここからいくつかの詳細:

注:最初の再描画要求が処理される前に、コンポーネントでrepaint()への複数の呼び出しが発生した場合、複数の要求がupdate()への単一の呼び出しに折りたたまれる可能性があります。複数のリクエストをいつ折りたたむかを決定するためのアルゴリズムは、実装に依存します。複数のリクエストが折りたたまれている場合、結果の更新長方形は、折りたたまれたリクエストに含まれている長方形の和集合に等しくなります。

于 2012-06-27T11:16:35.913 に答える
0

EDTにリクエストを送信する必要があります。

        EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            Rectangle r = myCurrentWorkingThread.getFinishedRectangle();
            myPainter.repaint(r);
        }
    });

アイデアは、ピクセルごとに再描画するのではなく、ワーカースレッドにより大きなチャンクを与えるということです。作業単位が終了するとすぐに、実際の作業を行うメインオブジェクト(myPainter)に通知します。この構成(EventQueue.invokeLater)は、イベントディスパッチャースレッド上にあることを保証します。

于 2012-06-27T11:25:12.070 に答える