0

私は単純なビデオ マニピュレータを作成しようとしているので、1 秒に数回、現在のフレームを処理するために新しいスレッド (現在 Runnable を実装している) を開始する必要がありますが、各スレッドが終了するまでにかかる時間の保証はありません。一度に実行できるスレッドの数をコンピューター上のプロセッサーの数に制限したい:

Runtime runtime = Runtime.getRuntime();
int nP = runtime.availableProcessors();  

ただし、フレームがドロップされないように、作成されたすべてのスレッドが順番に実行されることを保証する必要があります。

また、ユーザーがジョブをキャンセルしたときに実行するために残っているスレッドの数に基づいて、処理が完了するまでにかかる時間をユーザーに示して、予告編のないビデオ ファイルになってしまわないようにしたいと思います。

これは、futureTask、Exector、または ExecutorService の任意の組み合わせを使用して可能でしょうか?

ありがとう。

編集:

こんにちは皆さん、申し訳ありません。私が実際にやろうとしているのは、フレームを取得し、画像操作を実行してから、編集した映像を新しいファイルに保存することです。現時点では、再生中にこれを行っているため、タイマーによって呼び出されたときに各フレームが操作され、タイマーはスレッドを起動して画像をできるだけ早く処理しますが、今回行われる操作の数によって異なります。

次に、処理が間隔よりも長くかかる場合、最大効率的な数のスレッドのみが処理に使用され、この制限に達した後に作成されたスレッドは引き続き処理され、ドロップまたはガベージ コレクションされないことを確認したいと考えていました。 .

最初の 3 つのコメントを読むと、これはおそらく効率の悪い方法であることがわかります。UI の応答性を維持するためだけに 1 つのスレッドを使用するとうまくいくと思いますが、スレッドに画像を追加し続ける方法がわかりません。巨大なリストを使用せずに処理します。私はそれが次のようなものになると仮定しています:

メインクラスでは:

Timer actionPerformed {
    List.add(decodedImage);
}

実行可能なクラスで:

run() {
   while( timer.isRunning() ) {
     if( runCount >= list.size()-1 ) {
        try {
          Thread.sleep(500);
        } catch() {
             /* Catchy stuff */
        }
     } else {
        BufferedImage toProcess = list.get(runCount);
        /* Do Processing here */
        writeImageToStream();
        list.remove(runCount);
        runCount++;
     }
   }
}

これは正しいです?

編集2:

だから、これは私がこれまで持っているものです:

public class timerEncode {

     private long startTime;

     ActionListener goAction = new ActionListener() {
         public void actionPerformed( ActionEvent evt ) {
             BufferedImage decoded = getNextImage();
             long write_time = System.nanoTime();
             new doImages(decoded, write_time).run();
         }        
     };
     Timer goTimer = new Timer(40,goAction);

     private BufferedImage getNextImage() {
        /* Does inconsequential stuff to retrieve image from the stream*/
     }

     private void recBtnActionPerformed(java.awt.event.ActionEvent evt) {                                       
        startTime = System.nanoTime();
        goTimer.start();
     }

     private class doImages implements Runnable {
        final BufferedImage image;
        final long write_time;

        public doImages(BufferedImage image, long write_time) {
           this.image = image;
           this.write_time = write_time;
        }

        public void run() {
            BufferedImage out = toXuggleType(image, BufferedImage.TYPE_3BYTE_BGR);
            /* Other time consuming processy stuff goes here */
            /* Encode the frame to a video stream */
            writer.encodeVideo(0,out,write_time-startTime, TimeUnit.NANOSECONDS);
        }

        private BufferedImage toType(BufferedImage source, int type) {
            if( source.getType() != type ) {
                BufferedImage temp = new BufferedImage(source.getWidth(),source.getHeight(),type);
                temp.getGraphics().drawImage(source, 0, 0, null);
                source = temp;
            }
            return source;
        }
    }

}

これは、画像処理が単純な場合はうまく機能しますが、もう少し複雑になると、すぐに数十の同時スレッドが実行しようとすることに遭遇します。フレームごとに書き込み時間が指定されているため、フレームを順不同で書き込むと正しい場所に配置されると思うため、この場合の順序が特に重要かどうかはわかりませんが、これにはテストが必要です。

4

3 に答える 3

4

ただし、フレームがドロップされないように、作成されたすべてのスレッドが順番に実行されることを保証する必要があります。

あなたがここで言ったように、これを意味しますか?もしそうなら、フレーム1が終了するまでフレーム2の処理を開始できないことを理解しているので、これを実際にマルチスレッド化することはできません。その時点で、フレームを順次処理し、スレッド化を無視することもできます。

または、フレームを個別に処理できるが、順番に照合する必要があるなど、別のことを意味する場合、これは実行可能です。

いずれにせよ、「生の」スレッドを使用することに頼ることは、めったに必要ではなく、有益でもありません。他の人が指摘しているように、これを監視するには、高レベルの同時実行ユーティリティ (この場合は ThreadPoolExecutor が最適です) を使用してください。

Runnableグローバル変数を変更して処理の「結果」を返すことを意味するため、 a も正しい選択ではないようです。Callable代わりに、この処理を に変換して結果を返す方がよい場合があります。これにより、スレッドセーフの問題が取り除かれ、さまざまなフレームを問題なく一度に処理できるようになり、適切と思われる時点まで各結果の照合を延期できるようになります。

このようにしたい場合は、次のようなことができます。

// Create a thread pool with the given concurrency level
ExecutorService executor = Executors.newFixedThreadPool(Runtime.availableProcessors);

// Submit all tasks to the pool, storing the futures for further reference
// The ? here should be the class of object returned by your Callables
List<Future<?>> futures = new ArrayList<Future<?>>(NUM_FRAMES);
for (int i = 0; i < NUM_FRAMES; i++)
{
    futures.add(executor.submit(createCallableForFrame(i)));
}

// Combine results using future.get()
// e.g. do something with frames 2 and 3:
mergeFrames(futures.get(2).get(), futures.get(3).get());

// In practice you'd probably iterate through the Futures but it's your call!
于 2010-08-09T16:33:40.663 に答える
1

スレッドを継続的に起動するのは悪い考えです。パフォーマンスが大幅に低下します。必要なのは、スレッド プールと実行する一連のジョブ (Runnables) です。サイズ = プロセッサの数のスレッド プールを作成し、フレームを (ジョブとして) ジョブ キューに追加し続けると、スレッドはキューを順番に効率的に処理できます。

于 2010-08-09T15:57:37.263 に答える
0

[以前の回答の履歴を参照]

今何が起こっているかがわかります。Timerどのようにまたは動作するかについては完全にはわかりませんActionListeners(別の呼び出しが到着したときに前の呼び出しが終了していない場合に何が起こるかに関して)が、実際にはオブジェクトを同時に実行していないようですdoImages-Runnableオブジェクトを同時に実行するにはメソッドを呼び出すだけThread t = new Thread(runnableObject); t.start();の場合run()、(他のメソッド呼び出しと同様に) 順次実行されるため、実行するactionPerformed()までメソッドは終了しrun()ません。これにより、他の処理が妨げられる (または遅延される) かどうかはわかりませんActionEvents

他の人が示唆しているように、スレッドの数を制限するには、ThreadPoolExcecutorオブジェクトを使用する必要があります。これにより、actionPerformed()メソッドがすばやく戻り、doImagesオブジェクトを同時に実行し、キューを介してあまり多くのスレッドを使用しないようにすることができます。new doImages(decoded, write_time).run();に置き換えるだけですthreadPool.execute(new doImages(decoded, write_time))

プロセスを監視する方法については、 のgetQueue()メソッドを使用しThreadPoolExcecutorて、キューのサイズを取得して調べ、処理を待機しているフレームの数を確認できます。

于 2010-08-09T16:06:08.130 に答える