【问题标题】:Queue Threads if previous ones haven't finished如果之前的线程尚未完成,则排队线程
【发布时间】:2010-08-09 15:44:19
【问题描述】:

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

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

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

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

这是否可以使用 futureTask、Execector 或 ExecutorService 的任意组合?

谢谢。

编辑:

大家好,对不起,是的,这是相当糟糕的措辞。所以我实际上想要做的是获取框架,执行一些图像处理,然后将编辑后的素材保存回一个新文件。目前我正在播放期间执行此操作,因此当计时器调用每个帧时都会对其进行操作,然后计时器会启动一个线程以尽快处理图像,但取决于这次操作的数量会有所不同。

然后我想确保如果处理时间长于仅使用最大有效线程数进行处理的时间间隔,并且在达到此限制后创建的任何线程仍然被处理而不是被丢弃或垃圾收集。

阅读了前 3 个 cmets,我可以看到这可能是一种效率较低的方法,我想只有一个线程来保持 UI 响应会起作用,但我不确定如何继续将图像添加到它在不使用巨大列表的情况下处理的线程。我假设它会是这样的:

在主类中:

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

}

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

【问题讨论】:

    标签: java multithreading runnable


    【解决方案1】:

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

    你的意思是这里的措辞吗?如果是这样,那么您根本无法真正实现多线程,因为我知道在第 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!
    

    【讨论】:

    • java 7 中提供的 forkjoin 框架符合您的建议,但我喜欢并行性!
    【解决方案2】:

    连续启动线程是个坏主意 - 这会对性能造成很大影响。你想要的是一个线程池和一堆工作(Runnables)。如果您创建一个大小 = 处理器数量的线程池,并且只是继续将帧(作为作业)添加到作业队列中,那么您的线程将能够有效地按顺序通过队列处理它们的方式。

    【讨论】:

    • 谢谢,我已经听取了您关于不断创建新线程的建议,并尝试改进原始帖子中的解决方案。
    【解决方案3】:

    [查看历史以了解以前的回复]

    我知道现在发生了什么。我不完全确定 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))

    至于如何监控进程,可以使用ThreadPoolExcecutorgetQueue()方法来检索和检查队列的大小,看看有多少帧在等待处理。

    【讨论】:

    • 嗨,更新了我最初所做的一个精简示例
    猜你喜欢
    • 2017-04-17
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    • 1970-01-01
    • 2012-11-20
    • 1970-01-01
    • 2020-10-02
    相关资源
    最近更新 更多