队列线程如果以前没有完成

时间:2010-08-09 15:44:20

标签: java multithreading runnable

我正在尝试编写一个简单的视频操作器,所以我需要多次启动一个新线程(当前实现Runnable)来处理当前帧但是我不能保证每个线程需要多长时间才能完成因此,我想将可以一次运行的线程数限制为计算机上的处理器数量:

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

但我需要保证所有创建的线程都按顺序运行,因此不会删除任何帧。

我还想向用户显示完成处理需要多长时间,这取决于他们取消作业时剩余的线程数,这样他们就不会得到没有预告片的视频文件。

使用futureTask,Exector或ExecutorService的任意组合可以实现吗?

感谢。

编辑:

嗨,伙计们,对不起,那是非常严厉的措辞。所以我实际上要做的是获取帧,执行一些图像处理,然后将编辑后的素材保存回新文件。目前我在播放过程中这样做,因此当定时器调用每个帧时,每个帧都会被操作,然后定时器会启动一个线程来尽快处理图像,但是根据正在进行的操作次数,这个时间会有所不同。

我当时想要确保如果处理时间超过仅使用最大有效线程数进行处理的时间间隔,并且仍然处理了在达到此限制之后创建的任何线程仍未处理或收集垃圾。

阅读前3条评论后我可以看到这可能是一种效率较低的方式,我想只需要保持UI响应的一个线程就可以工作但是我不知道如何继续添加图像到它没有使用庞大的列表进行处理的线程。我假设它会像:

在主要课程中:

Timer actionPerformed {
    List.add(decodedImage);
}

在runnable类中:

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;
        }
    }

}

这在图像处理很简单的情况下运行正常,但是你很快会遇到数十个并发线程试图做它们的事情,因为它变得有点复杂,因此为什么我问如何限制并发线程数而不丢弃任何线程。在这种情况下,我不确定顺序特别重要,因为我认为按顺序编写帧会将它们放在正确的位置,因为每个帧都指定了写入时间,但这需要测试。

3 个答案:

答案 0 :(得分:4)

  

但我需要保证所有创建的线程都按顺序运行,因此不会删除任何帧。

你的意思是这个吗?如果是这样,那么你根本就不能真正多线程,因为据我所知,在第1帧完成之前你无法开始处理第2帧。此时,您也可以按顺序处理帧并忽略线程。

或者,如果您的意思是其他内容,例如框架可以独立处理但需要按顺序整理,那么这可能是可行的。

无论如何 - 使用“原始”线程很少是必需的或有益的。正如其他人所指出的那样,使用更高级别的并发实用程序(在这种情况下,ThreadPoolExecutor将是完美的)来监督它。

听起来Runnable也不是正确的选择,因为这意味着你通过改变一些全局变量来返回处理的“结果”。相反,最好将此处理转换为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!

答案 1 :(得分:1)

持续推出线程是一个坏主意 - 这是一个很大的性能影响。你想要的是一个线程池和一堆要做的工作(Runnables)。如果您创建一个大小=处理器数的线程池,并且只是继续将帧(作为作业)添加到作业队列,则您的线程将能够按顺序有效地处理它们在队列中的路径。

答案 2 :(得分:0)

[参见之前回复的历史记录]

我看到现在发生了什么。我不完全确定TimerActionListeners是如何工作的(如果前一个呼叫在另一个呼叫到达时尚未完成,会发生什么情况),但似乎您可能实际上并未运行同时运行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()的{​​{1}}方法来检索和检查队列的大小,以查看等待处理的帧数。