Потоки очереди, если предыдущие не закончились - PullRequest
0 голосов
/ 09 августа 2010

Я пытаюсь написать простой видео-манипулятор, поэтому несколько раз в секунду мне нужно запустить новый поток (в настоящее время реализующий Runnable) для обработки текущего кадра, но у меня нет гарантии, сколько времени займет завершение каждого потока, ипоэтому я хочу ограничить количество потоков, которые могут работать одновременно, количеством процессоров на компьютере:

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

Но я должен гарантировать, что все созданные потоки выполняются в последовательности, поэтому неткадры сброшены.

Я также хотел бы показать пользователю, сколько времени потребуется, чтобы завершить обработку, основываясь на количестве потоков, оставшихся для выполнения, когда они отменяют задание, чтобы у них не было видеофайла снет трейлера.

Возможно ли это, используя любую комбинацию futureTask, Exector или ExecutorService?

Спасибо.

РЕДАКТИРОВАТЬ:

Привет, ребята, извините, даэто было довольно плохо сформулировано.Итак, что я на самом деле пытаюсь сделать, это получить кадр, выполнить некоторые манипуляции с изображением, а затем сохранить отредактированный отснятый материал обратно в новый файл.В данный момент я делаю это во время воспроизведения, поэтому каждый кадр обрабатывается, когда он вызывается таймером, затем таймер запускает поток для максимально быстрой обработки изображения, но в зависимости от количества выполняемых манипуляций это время будет различным.

Затем я хотел убедиться, что если обработка занимает больше времени, чем интервал, то для обработки используется только максимально эффективное число потоков и что все потоки, созданные после достижения этого предела, все еще обрабатываютсяи не упал или мусор.

Прочитав первые 3 комментария, я вижу, что это, вероятно, менее эффективный способ сделать это, и я предполагаю, что будет работать только один поток, чтобы поддерживать отзывчивость пользовательского интерфейса, но я не уверен, как продолжать добавлятьизображения в потоке для его обработки без использования огромного списка.Я предполагаю, что это будет что-то вроде:

В основном классе:

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

}

Это прекрасно работает, когда обработка изображения проста, но вы скоронатолкнуться на десятки параллельных потоков, пытаясь сделать свое дело, поскольку это становится немного более сложным, поэтому я спрашивал, как ограничить количество одновременных потоков, не отбрасывая их.Я не уверен, что порядок особенно важен в этом случае, так как я думаю, что написание кадров не по порядку поместит их в правильное место, так как время записи указано для каждого кадра, но это требует тестирования.

Ответы [ 3 ]

4 голосов
/ 09 августа 2010

Но я должен гарантировать, что все созданные потоки выполняются в последовательности, поэтому кадры не удаляются.

Вы имеете это в виду, когда формулируете это здесь?Если это так, то вы вообще не можете многопоточность, так как я понимаю, что вы не можете начать обработку кадра 2, пока кадр 1 не закончится.В этот момент вы также можете последовательно обрабатывать кадры и игнорировать многопоточность.

В качестве альтернативы, если вы имеете в виду что-то еще, например, что кадры могут обрабатываться независимо, но должны быть сопоставляя в последовательности, тогда это может быть выполнимо.

В любом случае - использование «сырых» потоков редко требуется или полезно.Как уже отмечали другие, используйте высокоуровневые утилиты параллелизма (в этом случае 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 голос
/ 09 августа 2010

Плохо запускать потоки непрерывно - это большой удар по производительности.Вам нужен пул потоков и куча заданий (Runnables).Если вы создадите пул потоков размером = число процессоров и просто продолжаете добавлять кадры (в виде заданий) в очередь заданий, ваши потоки смогут эффективно обрабатывать свой путь в очереди по порядку.

0 голосов
/ 09 августа 2010

[см. Историю предыдущих ответов]

Я вижу, что происходит сейчас.Я не совсем уверен в том, как 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 для получения и проверки размераочередь для просмотра количества кадров, ожидающих обработки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...