Сделайте это красиво: обрабатывать массив одновременно - PullRequest
3 голосов
/ 27 мая 2009

Я хочу преобразовать этот линейный цикл в параллельный:

for(Item item : ItemList) {
    processItem(item);
}

Это действительно самый короткий способ сделать это?

class Worker implements Runnable {
    Item item;
    Worker(Item item) {
        this.item = item;
    }
    public void Run() {
        processItem(item);
    }
}

ExecutorService exec = Executors.newFixedThreadPool(THREADPOOL_SIZE);
for(Item item : ItemList) {
    exec.execute(new Worker(item));
}
exec.shutdown();

boolean properFinish = false;
try {
    properFinish = exec.awaitTermination(50, TimeUnit.SECONDS);
} catch (InterruptedException e) { 
    Thread.currentThread().interrupt();
}

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

ОБНОВЛЕНИЕ: Просто понял, что я немного глуп, так как довольно просто использовать анонимный класс в этом примере:

for(final Item item : ItemList) {
    exec.execute(new Runnable() {
        public void run() {
            processItem(item);
        }
    });
}

В моем исходном коде цикл был простым for (i=0; i<=ItemList.length(); i++), и я не мог придумать, как сделать финал каким-либо образом, который имеет смысл. Я предполагаю, что использование цикла «для каждого» улучшит ситуацию.

И все же, есть ли способ избавиться от остальной части шаблона?

ОБНОВЛЕНИЕ 2 : Использование ExecutorCompletionService при условии, что processItem возвращает результат.

ExecutorService exec = Executors.newFixedThreadPool(THREADPOOL_SIZE);
CompletionService<ResultType> ecs = new ExecutorCompletionService<ResultType>(executor);
for(final Item item : ItemList) {
    ecs.submit(new Callable<ResultType>() {
        public ResultType call() {
            return processItem(item);
        }
    });
}

for(Item item : item) {
    // Do whatever with the results
    ecs.take().get();
}

Это выглядит лучше.

Ответы [ 2 ]

7 голосов
/ 27 мая 2009

Проверьте ExecutorCompletionService в JDK 6 - вместо того, чтобы крутить свою собственную. Это позволяет вам выполнить несколько потоков для назначенной задачи и получить результаты каждого потока по мере его обработки.

1 голос
/ 08 июня 2009

Если вам нужны результаты и вы хотите, чтобы они были в том же порядке, что и отправленные, то, возможно, сделайте что-то вроде:

List<Future<ResultType>> futures = new ArrayList<Future<ResultType>>();
for(final Item item : items) {
  futures.add(executor.submit(...))
}
List<ResultType> results = new ArrayList<ResultType>();
for(Future<ResultType> future : futures) {
  results.add(future.get());
}
return results;

РЕДАКТИРОВАТЬ: Если у вас есть некоторые чрезмерные инженерные, вы можете вернуть итератор вместо выполнения второго цикла (чтобы вызывающая сторона могла начать обработку результатов, прежде чем все станут доступны):

return Iterators.transform(results, new Function<Future<ResultType>,ResultType>() {
   public ResultType apply(Future<ResultType> future) {
       return future.get();
   }
})

с использованием "Итераторов" из библиотеки коллекции Google. Другая возможность - просто вернуть список фьючерсов и позволить вызывающему сделать ожидание.

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