Як створити кілька потоків для кожного елемента запиту


9

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

List<String> orders = Arrays.asList("order1", "order2", 
                   "order3", "order4", "order1");

Поточне послідовне виконання:

orders.stream().forEach(order -> {
    rules.forEach(rule -> {
        finalList.add(beanMapper.getBean(rule)
                .applyRule(createTemplate.apply(getMetaData.apply(rule), command),
                           order));
    });
});

Я спробував використовувати:

orders.parallelStream().forEach(order -> {}} // code snippet.

Але це змінює правила.forEach (правило -> {}} порядок.

Наприклад:
Введення:

 List<String> orders = Arrays.asList("order1", "order2", 
                         "order3", "order4", "order1");
 List<String> rules = Arrays.asList("rule1", "rule2", "rule3");

Очікуваний вихід:

order1 with rule1, rule2, rule3
order2 with rule1, rule2, rule3

Фактичний вихід з parallelStream():

order1 with rule3, rule1, rule2
order1 with rule2, rule1, rule3

Мене не турбує порядок наказів , але мене турбує е-порядок правил . Замовлення можуть оброблятись у будь-якому порядку, але правила повинні виконуватись у тому самому порядку для кожного замовлення.

Будь ласка, допоможіть.

Відповіді:


4

Ви можете використовувати:

orders.stream().parallel().forEachOrdered(// Your rules logic goes here. )

ForEachOrряд гарантує збереження порядку Потоку.

Тож для довідки:

orders.stream().parallel().forEachOrdered( order -> {

            rules.stream().parallel().forEachOrdered ( rule -> {

                 System.out.println( " Order : " + order + " rule :" + rule);
            });

        });

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

Вихідні дані

 Order : order1 rule :rule1
 Order : order1 rule :rule2
 Order : order1 rule :rule3
 Order : order2 rule :rule1
 Order : order2 rule :rule2
 Order : order2 rule :rule3
 Order : order3 rule :rule1
 Order : order3 rule :rule2
 Order : order3 rule :rule3
 Order : order4 rule :rule1
 Order : order4 rule :rule2
 Order : order4 rule :rule3
 Order : order1 rule :rule1
 Order : order1 rule :rule2
 Order : order1 rule :rule3

Дякую за відповідь. forEachOrdered гарантує порядок потоку, але тоді це також уповільнює продуктивність. Я спробував це, і додаток займає час, подібний до послідовної обробки. stream (). Паралельний & forEachOrряд не компліментує один одного.
майнк біш

Але я згоден, нам потрібно зробити повний аналіз затримки перед цим.
Прамод С. Нікам

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

1
Уважно слідкуйте за цією ниткою, щоб отримати краще рішення для цього.
Прамод С. Нікам

Чи можна досягти паралельної обробки за допомогою ExecutorService?
mayank bisht

1

Ви додаєте елементи в finalListрізні потоки одночасно. Це викликає змішування результатів застосування правил до різних замовлень (правила не групуються за їхніми наказами).

Ви можете виправити це, створивши тимчасовий список для кожного, orderа потім синхронно об'єднавши всі тимчасові списки в finalList.

Ось як це можна зробити за допомогою Stream-API (Java 9+):

List<AppliedRule> finalList = orders.parallelStream().map(order ->
        rules.stream().map(rule -> applyRule(order, rule)).collect(Collectors.toList())
).collect(Collectors.flatMapping(Collection::stream, Collectors.toList()));

Примітка: Collectors.flatMapping()тут використовується замість простого flatMapсинхронного виконання плоского відображення під час збору потоку.


Аналог Java 8:

List<AppliedRule> finalList = orders.parallelStream().map(order ->
        rules.stream().map(rule -> applyRule(order, rule)).collect(Collectors.toList())
).collect(Collectors.toList())
        .stream()
        .flatMap(Collection::stream)
        .collect(Collectors.toList());

Дякую за відповідь. Я спробував ваш підхід, і я отримую java.util.ConcurrentModificationException: null
mayank bisht

finalList = orders.parallelStream () .map (order -> rules.stream () .map (rule -> beanMapper.getBean (rule) .applyRule (createTemplate.apply (getMetaData.apply (правило), command), order), order)) .collect (Collectors.toList ())). collection (Collectors.toList ()). stream (). flatMap (Колекція :: stream) .collect (Collectors.toList ());
mayank bisht

@mayankbisht, це означає, що beanMapper.getBean(rule) .applyRule(createTemplate.apply(getMetaData.apply(rule), command), order)це не є чистою функцією, тому її не можна використовувати паралельно. Спробуйте усунути з нього всі побічні ефекти; ConcurrentModificationExceptionСлід стека може допомогти їх знайти.
Бананон

0

Чи буде це працювати?

final int rulesSize = rules.size();
AtomicInteger atomicInteger = new AtomicInteger(0);
orders.stream().parallel().forEach(order -> {
    IntStream.range(0, rulesSize).parallel().forEach( i -> {
        synchronized (atomicInteger) {
            System.out.println(" Order : " + order + " rule :" + rules.get(atomicInteger.getAndIncrement() % rulesSize));
        }
    });
});

Вихідні дані

 Order : order1 rule :rule1
 Order : order4 rule :rule2
 Order : order1 rule :rule3
 Order : order3 rule :rule1
 Order : order3 rule :rule2
 Order : order3 rule :rule3
 Order : order2 rule :rule1
 Order : order2 rule :rule2
 Order : order2 rule :rule3
 Order : order1 rule :rule1
 Order : order1 rule :rule2
 Order : order4 rule :rule3
 Order : order1 rule :rule1
 Order : order4 rule :rule2
 Order : order1 rule :rule3

0

Порядок замовлень може бути будь-яким, але порядок правил не повинен змінюватися. Також для певного правила замовлення слід надходити в групі.

Якщо це так, немає місця для фактичного паралелізму.

Коли

order1-rule1
order1-rule2
order2-rule1
order2-rule2

і

order2-rule1
order2-rule2
order1-rule1
order1-rule2

є єдиними дійсними пробіжками для 2 замовлень та 2 правил,
і

order1-rule1
order2-rule1
order1-rule2
order2-rule2

вважається недійсним, тобто не паралелізм, а просто рандомізація orders, імовірно, без виграшу. Якщо вам "нудно", коли ви order1прийдете спочатку весь час, ви можете перетасувати список, але це все:

public static void main (String[] args) throws java.lang.Exception
{
    List<String> orders = Arrays.asList("order1", "order2", "order3", "order4");
    List<String> rules = Arrays.asList("rule1", "rule2", "rule3");
    Collections.shuffle(orders);
    orders.forEach(order->{
        rules.forEach(rule->{
            System.out.println(order+"-"+rule);
        });
    });
}

Навіть не потрібно потокове передавання, просто дві вкладені петлі. Тест: https://ideone.com/qI3dqd

order2-rule1
order2-rule2
order2-rule3
order4-rule1
order4-rule2
order4-rule3
order1-rule1
order1-rule2
order1-rule3
order3-rule1
order3-rule2
order3-rule3


Оригінальна відповідь

Але це змінює правила.forEach (правило -> {}} порядок.

Ні, це не є. В orderи можуть перекривати один одного, але порядок ruleз для кожного замовлення зберігаються. Чому б непаралель forEachробив щось інше?

Приклад коду:

public static void main (String[] args) throws java.lang.Exception
{
    List<String> orders = Arrays.asList("order1", "order2", "order3", "order4");
    List<String> rules = Arrays.asList("rule1", "rule2", "rule3");
    orders.stream().parallel().forEach(order->{
        rules.forEach(rule->{
            System.out.println(order+"-"+rule);
        });
    });
}

Тест: https://ideone.com/95Cybg
Приклад виведення:

order2-rule1
order2-rule2
order2-rule3
order1-rule1
order1-rule2
order1-rule3
order4-rule1
order4-rule2
order4-rule3
order3-rule1
order3-rule2
order3-rule3

Порядок orders змішаний, але rules завжди 1-2-3. Я думаю, що ваш результат просто приховав пар (насправді ви не показали, як це генерується).

Звичайно, його можна продовжити з деякими затримками, тому обробка orders фактично перекриється:

public static void delay(){
    try{
        Thread.sleep(ThreadLocalRandom.current().nextInt(100,300));
    }catch(Exception ex){}
}

public static void main (String[] args) throws java.lang.Exception
{
    List<String> orders = Arrays.asList("order1", "order2", "order3", "order4");
    List<String> rules = Arrays.asList("rule1", "rule2", "rule3");
    orders.stream().parallel().forEach(order->{
        rules.forEach(rule->{
            delay();
            System.out.println(order+"-"+rule);
        });
    });
}

Тест: https://ideone.com/cSFaqS
Приклад виведення:

order3-rule1
order2-rule1
order2-rule2
order3-rule2
order3-rule3
order2-rule3
order1-rule1
order4-rule1
order1-rule2
order4-rule2
order4-rule3
order1-rule3

Це може бути те, що ви бачили, тільки без orderxчастини. З orderвидимим s можна відстежити, що ruleпродовжують надходити як 1-2-3, заorder . Також ваш список прикладів містив order1двічі, що точно не допомогло побачити, що відбувається.


Дякую за відповідь. Вищенаведений вихід може бути правильним для меншої кількості замовлень. Але якщо ви збільшите замовлення, то отримаєте інший вихід. Наприклад (order4-rule1 order4-rule2 order4-rule1) (order1-rule1 order1-rule2) (order3-rule1 order3-rule2) (order4-rule1 order4-rule2 order4-rule1 order4-rule2).
mayank bisht

Порядок замовлень може бути будь-яким, але порядок правил не повинен змінюватися. Також для певного правила замовлення слід надходити в групі. Для екс. (order1- правило 1 order1-rule2 order1-rule3), а не (order1-rule1 order2-rule1 order1-rule2 order1-rule3).)
майнк біш

@mayankbisht Я думаю, що ці обмеження просто не дозволяють проводити паралельну обробку. Дивіться оновлену відповідь (нову частину я написав на початку).
тевемадар

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

@mayankbisht ви могли б описати, чому orders не можуть перекриватися ( ruleможливо, вони є державними і існують у обмеженій кількості примірників, можливо, лише в одній?). Але взагалі немає паралелізму, коли речі не працюють паралельно, тобто вся суть паралелізму.
тевемадар

0

Якщо ви не проти спробувати бібліотеку сторонніх розробників. Ось зразок з моєю бібліотекою: abacus-util

StreamEx.of(orders).parallelStream().forEach(order -> {}}

Ви навіть можете вказати номер потоку:

StreamEx.of(orders).parallelStream(maxThreadNum).forEach(order -> {}}

Порядок ruleволі буде дотримано .

До речі, оскільки він знаходиться в паралельному потоці, фрагмент коду, ...finalList.add(...швидше за все, не буде працювати. Я думаю, що краще зібрати результат для переліку:

StreamEx.of(orders).parallelStream().map/flatMap(order -> {...}}.toList()

це також можливо, навіть якщо ви хочете orderчомусь зберегти порядок :

StreamEx.of(orders).indexed().parallelStream()
      .map/flatMap(order -> {...}}.sortedBy(...index).toList()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.