2010-08-09 70 views
0

我想寫一個簡單的視頻操作器,所以每秒幾次我需要啓動一個新線程(當前實現Runnable)來處理當前幀,但我沒有保證每個線程需要多長時間才能完成,因此我想限制可以對處理器的計算機上的數字同時運行的線程數量:隊列線程,如果以前的還沒有完成

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

但我要保證所有線程都創建的按順序運行,因此不會丟幀。

我還想向用戶顯示完成處理需要多長時間,這取決於在取消作業時剩下的線程數量,以便它們不會以沒有預告片的視頻文件結束。

使用futureTask,Exector或ExecutorService的任意組合可以實現這種可能性嗎?

謝謝。

編輯:

傢伙嗨,對不起啊,這是相當嚴重的措辭。所以我實際上想要做的是獲取框架,執行一些圖像處理,然後將編輯後的素材保存回新文件。此刻我在回放過程中這樣做,因此每個幀在被定時器調用時都會被操作,定時器會啓動一個線程以儘快處理圖像,但取決於此次操作的次數會有所不同。

我當時想確保如果處理花費的時間比只有最大有效數量的線程用於處理的時間間隔長,並且在達到此限制後創建的任何線程仍然處理而不處理丟棄或垃圾收集。

閱讀前3條評論我可以看到這可能是一個效率較低的方法,我想只有一個線程只是爲了保持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; 
     } 
    } 

} 

此工作正常時,圖像處理很簡單,但你很快就會碰到幾十個併發線程試圖做他們的事情,因爲它變得有點複雜,因此我爲什麼要問如何限制線程的併發數量而不放棄任何。我不確定訂單在這種情況下特別重要,因爲我認爲按順序編寫幀會將它們放在正確的位置,因爲每個幀都指定了寫入時間,但這需要測試。

回答

1

不斷推出主題是一個壞主意 - 這是一個很大的表現。你想要的是一個線程池和一堆工作(Runnables)。如果您創建了size =處理器數量的線程池,並且只需將作爲作業的框架添加到作業隊列中,則您的線程將能夠按順序有效地處理隊列。

+0

謝謝,我已經採取了你的船上建議約不斷創造新的線程,並試圖改善原來的解決方案帖子。 – drent 2010-08-09 17:18:26

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()方法ThreadPoolExcecutor檢索並檢查隊列的大小,以查看有多少幀正在等待處理。

+0

嗨,謝謝。爲了清晰起見,我編輯了原始帖子。 – drent 2010-08-09 17:17:17

+0

嗨,更新了一個我最初做了什麼的減少的例子 – drent 2010-08-11 08:10:29

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! 
+0

在java 7中提供的forkjoin框架工作確實如你所說,但我喜歡並行性! – 2010-08-10 13:53:04

相關問題