Як масштабувати нитки відповідно до ядер CPU?


107

Я хочу вирішити математичну задачу з декількома потоками на Java. мою математичну проблему можна розділити на робочі одиниці, які я хочу вирішити в декількох потоках.

Я не хочу мати фіксовану кількість потоків, що працюють над ним, але натомість кількість потоків, що відповідає кількості ядер CPU. Моя проблема полягає в тому, що я не зміг знайти простий підручник в Інтернеті для цього. Я знайшов лише приклади з фіксованими нитками.

Як це можна зробити? Чи можете ви навести приклади?

Відповіді:


119

Ви можете визначити кількість процесів, доступних для віртуальної машини Java, використовуючи статичний метод виконання, доступнийПроцесори . Визначивши кількість доступних процесорів, створіть цю кількість потоків і відповідно розділіть свою роботу.

Оновлення : Для подальшого уточнення, Нитка - це просто Об'єкт на Java, тому ви можете створити її так само, як і будь-який інший об’єкт. Отже, скажімо, що ви викликаєте вищевказаний метод і виявляєте, що він повертає 2 процесора. Дивовижно. Тепер ви можете створити цикл, який генерує нову нитку, і розщеплює роботу для цього потоку та запускає нитку. Ось псуедокод для демонстрації того, що я маю на увазі:

int processors = Runtime.getRuntime().availableProcessors();
for(int i=0; i < processors; i++) {
  Thread yourThread = new AThreadYouCreated();
  // You may need to pass in parameters depending on what work you are doing and how you setup your thread.
  yourThread.start();
}

Щоб отримати додаткові відомості про створення власної теми, зверніться до цього підручника . Крім того, ви можете поглянути на "Об'єднання ниток" для створення ниток.


17
Це в основному правильно, але будьте обережні щодо продуктивності процесорів, що продаються з використанням "Hyper-Threading" від Intel. На чотирьохядерному процесорі це повернеться 8 замість 4, але продуктивність може насправді почати знижуватися після 4-х потоків - так мені кажуть мої власні орієнтири :)
xcut

Привіт, добре, не знав, що це можливо. але коли я розділив одне завдання на кілька робочих частин, і мені потрібно вирішити частину для остаточного робочого етапу, як це робиться? Коли у мене є кілька "yourThreads", як я можу використовувати для цього join (), тому що я не бачу, як можна виділити ці кілька потоків? :) BTW: ваше посилання на Об'єднання ниток привело мене до ibm.com/developerworks/library/j-jtp0730.html :)
Андреас Хорніг

5
Подивіться на приклад тут: java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/… Це підкаже вам більш спрощений спосіб створення та управління пулом потоків ... Це може здатися Спочатку складніше, але як і у більшості речей, це складніше, бо якби це було простіше, ти швидше потрапив до обмежень.
Білл К

62

Ви, мабуть, хочете подивитися також на java.util.concurrent Framework для цього матеріалу. Щось на зразок:

ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// Do work using something like either
e.execute(new Runnable() {
        public void run() {
            // do one task
        }
    });

або

    Future<String> future = pool.submit(new Callable<String>() {
        public String call() throws Exception {
            return null;
        }
    });
    future.get();  // Will block till result available

Це набагато приємніше, ніж справлятися з власними пулами ниток тощо.


Привіт, DaveC, гммм, не знав цього раніше, тому я буду дивитись на це. І це можна масштабувати відповідно до наявних процесорних ядер? Тому що я не бачу цього у вас короткими прикладами. З найкращими побажаннями, Андреас
Андреас Хорніг

3
java.util.concurrent дуже масштабований
Крістофер Івз

4
Пул фіксованого розміру з кількістю доступних процесорів часто є оптимальним для процесорів, пов'язаних з процесором. Перший приклад - це все, що вам потрібно зробити.
Пітер Лоурі

1
Як зазначено в першому коментарі прийнятої відповіді, було б краще використовувати половину кількості повідомлених "Процесорів" з двох причин: 1. Якщо у вас є гіперточка, реальна кількість процесорів - це половина тієї, що повідомляється. , і 2. вона дає деяку потужність процесора для решти системи (ОС та інші програми).
Матьє

10

Варіант 1:

newWorkStealingPool відExecutors

public static ExecutorService newWorkStealingPool()

Створює пул потоків крадіжок роботи, використовуючи всі доступні процесори як його цільовий рівень паралелізму.

За допомогою цього API вам не потрібно передавати кількість ядер ExecutorService.

Реалізація цього API з grepcode

/**
     * Creates a work-stealing thread pool using all
     * {@link Runtime#availableProcessors available processors}
     * as its target parallelism level.
     * @return the newly created thread pool
     * @see #newWorkStealingPool(int)
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

Варіант 2:

newFixedThreadPool API від Executorsабо other newXXX constructors, який повертаєтьсяExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

замініть nThreads на Runtime.getRuntime().availableProcessors()

Варіант 3:

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue)

передавати Runtime.getRuntime().availableProcessors()як параметр до maximumPoolSize.


8

Дог Леа (автор одночасного пакету) має цей документ, який може бути актуальним: http://gee.cs.oswego.edu/dl/papers/fj.pdf

Рамка Fork Join була додана в Java SE 7. Нижче ще кілька посилань:

http://www.ibm.com/developerworks/java/library/j-jtp11137/index.html Стаття Брайана Геца

http://www.oracle.com/technetwork/articles/java/fork-join-422606.html


4

Стандартний спосіб - метод Runtime.getRuntime (). AvailableProcessors (). У більшості стандартних процесорів ви повернете тут оптимальне число потоків (що не є фактичним числом ядер CPU). Тому це те, що ви шукаєте.

Приклад:

ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

НЕ забудьте вимкнути послугу виконавця на зразок цієї (або програма не вийде):

service.shutdown();

Ось лише короткий опис, як створити майбутній MT-код (офтопік, для ілюстрації):

CompletionService<YourCallableImplementor> completionService = 
    new ExecutorCompletionService<YourCallableImplementor>(service);
    ArrayList<Future<YourCallableImplementor>> futures = new ArrayList<Future<YourCallableImplementor>>();
    for (String computeMe : elementsToCompute) {
        futures.add(completionService.submit(new YourCallableImplementor(computeMe)));
    }

Тоді вам слід буде відслідковувати кількість результатів, які ви очікуєте, і отримувати їх так:

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}

2
виклик відключення слід спробувати остаточно
Крістоф Руссі

1
@ChristopheRoussy Ви дуже праві, я відповідно змінив фрагмент, дякую!
fl0w

3

Для класу Runtime існує метод, який називається availableProcessors (). Ви можете використовувати це, щоб визначити, скільки у вас процесорів. Оскільки ваша програма пов'язана з процесором, ви, ймовірно, хочете мати (максимум) один потік на доступний процесор.


Привіт, Джейсон та Ерік (я використовую один коментар для обох ваших відповідей, тому що він в основному однаковий). добре, це приємно перевірити, але це була б перша частина. Коли я маю кількість ядер, я повинен мати потоки такою ж змінною, як ця кількість ядер. Я спробував цей приклад перед openbook.galileodesign.de/javainsel5/… (німецька!), І він використовує фіксовану нитку. Але я хочу мати те саме програмування, використовуючи 2 ядра в двоядерному середовищі і 4 ядра в чотирьохядерному середовищі. Я не хочу змінювати це вручну. Чи можливо це? ДЯКУЮ! :)
Андреас Горніг

@Andreas - Перегляньте оновлення, зроблені мною в публікації. Я думаю, що це допоможе з’ясувати це питання.
JasCav
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.