Come creare più thread per ciascun elemento della richiesta


9

Sto cercando di elaborare il codice seguente usando il multithreading a livello di ordine.

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

Esecuzione sequenziale corrente:

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

Ho provato ad usare:

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

Ma sta cambiando l'ordine rules.forEach (rule -> {}} .

Ad esempio:
input:

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

Uscita prevista:

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

Uscita effettiva con parallelStream():

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

Non sono preoccupato per l'ordine degli ordini , ma sono preoccupato per l'ordine delle regole . Gli ordini possono essere elaborati in qualsiasi ordine, ma le regole devono essere eseguite nello stesso ordine per ciascun ordine.

Per favore aiuto.

Risposte:


4

Puoi usare :

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

ForEachOrdered garantisce di mantenere l'ordine del flusso.

Quindi per il tuo riferimento:

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

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

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

        });

Nota: mentre possiamo farlo, le prestazioni dovrebbero essere attentamente osservate perché il parellelismo e l'ordine non si sposano molto bene!

Produzione

 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

Grazie per la risposta. forEachOrdered garantisce l'ordine del flusso, ma poi rallenta anche le prestazioni. L'ho provato e l'applicazione sta impiegando un tempo simile all'elaborazione sequenziale. stream (). parallel & forEachOrdered non si completano a vicenda.
Mayank Bisht,

Sì, sono d'accordo che dobbiamo fare un'analisi completa della latenza prima di farlo.
Pramod S. Nikam,

Sì, sto ottenendo le stesse prestazioni usando questo, non c'è alcun miglioramento.
Mayank Bisht,

1
Seguendo da vicino questo thread per ottenere una soluzione migliore per farlo.
Pramod S. Nikam,

Posso ottenere l'elaborazione parallela utilizzando ExecutorService?
Mayank Bisht,

1

Aggiungete elementi al finalListda diversi thread contemporaneamente. Ciò sta causando la miscelazione dei risultati dell'applicazione di regole a diversi ordini (le regole non vengono raggruppate in base ai loro ordini).

È possibile risolverlo creando un elenco temporaneo per ciascuno ordere quindi unendo in modo sincrono tutti gli elenchi temporanei in a finalList.

Ecco come puoi farlo utilizzando 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()));

Nota: Collectors.flatMapping()viene utilizzato qui anziché semplice flatMapmappatura piatta in modo sincrono durante la raccolta di flussi.


Analogo 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());

Grazie per la risposta. Ho provato il tuo approccio e sto ottenendo java.util.ConcurrentModificationException: null
mayank bisht

finalList = order.parallelStream () .map (order -> rules.stream () .map (rule -> beanMapper.getBean (rule) .applyRule (createTemplate.apply (getMetaData.apply (rule), command), order)) .Raccogliere (Collectors.toList ())) raccoglie (Collectors.toList ()) flusso () flatMap (Collection :: stream) .Raccogliere (Collectors.toList (.))..;
Mayank Bisht,

@mayankbisht, questo significa che beanMapper.getBean(rule) .applyRule(createTemplate.apply(getMetaData.apply(rule), command), order)non è una funzione pura, quindi non può essere utilizzata in parallelo. Prova a rimuovere tutti gli effetti collaterali da esso; ConcurrentModificationExceptionla traccia dello stack può aiutare a individuarli.
Bananon,

0

Funzionerà?

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

Produzione

 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

L'ordine degli ordini può essere qualsiasi cosa, ma l'ordine delle regole non dovrebbe cambiare. Anche per una particolare regola d'ordine dovrebbe venire in un gruppo.

In tal caso, non c'è spazio per il parallelismo reale.

quando

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

e

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

sono le uniche corse valide per 2 ordini e 2 regole
e

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

è considerato non valido, cioè non parallelismo, solo randomizzazione di orders, presumibilmente senza guadagno. Se sei "annoiato" di order1venire sempre per primo, puoi mescolare l'elenco, ma questo è tutto:

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

Non è nemmeno necessario lo streaming, solo due loop nidificati. Test: 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


Risposta originale

Ma sta cambiando l'ordine rules.forEach (rule -> {}}.

No non lo fa. Le orders possono sovrapporsi, ma l'ordine di rules per ciascun ordine viene mantenuto. Perché un non parallelo dovrebbe forEachfare qualcos'altro?

Codice di esempio:

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

Test: https://ideone.com/95Cybg
Esempio di output:

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

L'ordine di orders è misto, ma i rules sono sempre 1-2-3. Penso che il tuo output abbia semplicemente nascosto gli accoppiamenti (in realtà non hai mostrato come è stato generato).

Naturalmente può essere esteso con alcuni ritardi, quindi l'elaborazione di orders si sovrapporrà effettivamente:

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

Test: https://ideone.com/cSFaqS
Esempio di output:

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

Questo potrebbe essere qualcosa che hai visto, senza la orderxparte. Con la orders visibile può essere rintracciato che rulecontinua a venire come 1-2-3, perorder . Inoltre, la tua lista di esempi conteneva order1due volte che sicuramente non aiutavano a vedere cosa stava succedendo.


Grazie per la risposta. L'output di cui sopra potrebbe essere corretto per un numero inferiore di ordini. Ma se aumenti gli ordini, otterrai un output diverso. Per es. (Order4-rule1 order4-rule2 order4-rule1) (order1-rule1 order1-rule2) (order3-rule1 order3-rule2) (order4-rule1 order4-rule2 order4-rule1 order4-rule2).
Mayank Bisht

L'ordine degli ordini può essere qualsiasi cosa, ma l'ordine delle regole non dovrebbe cambiare. Anche per una particolare regola d'ordine dovrebbe venire in un gruppo. Per es. (order1- rule 1 order1-rule2 order1-rule3) e non (order1-rule1 order2-rule1 order1-rule2 order1-rule3).)
mayank bisht

@mayankbisht Penso che queste restrizioni semplicemente non consentano l'elaborazione parallela. Vedi la risposta aggiornata (ho scritto la nuova parte all'inizio).
martedì

Sì, lo capisco ed è per questo che ho pubblicato questa domanda qui. Pensavo che forse ci sarebbe stato un altro modo per farlo, o forse potremmo cambiare l'algo
mayank bisht

@mayankbisht potresti descrivere il motivo orderper cui non possono sovrapporsi (quelli sono ruleforse stati, ed esistono in un numero limitato di copie, forse solo uno?). Ma generalmente non c'è parallelismo senza cose che corrono in parallelo, questo è il punto fondamentale del parallelismo dopo tutto.
martedì

0

Se non ti dispiace provare la libreria di terze parti. Ecco un esempio con la mia libreria: abacus-util

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

E puoi anche specificare il numero di thread:

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

L'ordine di rulesarà mantenuto.

A proposito, dal momento che è in flusso parallelo, ...finalList.add(...molto probabilmente il pezzo di codice non funzionerà. Penso che sia meglio raccogliere il risultato per elencarlo:

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

è anche fattibile anche se vuoi mantenere l'ordine di orderper qualche motivo in seguito:

StreamEx.of(orders).indexed().parallelStream()
      .map/flatMap(order -> {...}}.sortedBy(...index).toList()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.