Utilizzando AsParallel()/Parallelo.ForEach() linee guida?

Cercando un po ‘ di consigli sull’uso della AsParallel() o Parallel.ForEach() per accelerare.

Vedere il metodo che ho (semplificato/imbastardita per questo esempio) di seguito.

Ci vuole un elenco come “US, FR, APAC”, dove il “MEA” è un alias per magari altri 50 “US, FR, JP, IT, GB” etc. paesi. Il metodo deve prendere “US, FR, APAC”, e convertirlo in un elenco di “NOI”, “FR”, oltre a tutti i paesi che sono in “MEA”.

private IEnumerable<string> Countries (string[] countriesAndAliases)
{
    var countries = new List<string>();

    foreach (var countryOrAlias in countriesAndAliases)
    {
        if (IsCountryNotAlias(countryOrAlias))
        {
            countries.Add(countryOrAlias);
        }
        else 
        {
            foreach (var aliasCountry in AliasCountryLists[countryOrAlias]) 
            {
                countries.Add(aliasCountry);
            }
        }
    }

    return countries.Distinct();
}

È parallelizzato semplice come cambiare a cosa c’è sotto? C’è di più nuance utilizzando AsParallel() di questo? Dovrei usare Parallel.ForEach() invece di foreach? Quali regole devo usare quando la parallelizzazione di foreach loop?

private IEnumerable<string> Countries (string[] countriesAndAliases)
{
    var countries = new List<string>();

    foreach (var countryOrAlias in countriesAndAliases.AsParallel())
    {
        if (IsCountryNotAlias(countryOrAlias))
        {
            countries.Add(countryOrAlias);
        }
        else 
        {
            foreach (var aliasCountry in AliasCountryLists[countryOrAlias].AsParallel()) 
            {
                countries.Add(aliasCountry);
            }
        }
    }

    return countries.Distinct();
}
  • C’è bisogno di accelerare?
  • In realtà, no. Sto solo cercando di utilizzare questo come un esercizio per imparare nuove parallelizzazione in .NET. 🙂



4 Replies
  1. 68

    Diversi punti.

    appena iscritto countriesAndAliases.AsParallel() è inutile. AsParallel() fa parte di query Linq che viene dopo è da eseguire in parallelo. Parte è vuota, quindi non servono a niente.

    in genere non si dovrebbe repaci foreach con Parallel.ForEach(). Ma attenzione a non thread-safe codice! Si hanno. Non si può semplicemente avvolgere in foreach perché List<T>.Add non è thread-safe stesso.

    così si dovrebbe fare in questo modo (mi dispiace, io non l’ho provato, ma da compilare):

            return countriesAndAliases
                .AsParallel()
                .SelectMany(s => 
                    IsCountryNotAlias(s)
                        ? Enumerable.Repeat(s,1)
                        : AliasCountryLists[s]
                    ).Distinct();

    Modifica:

    Bisogna essere sicuri di due cose:

    1. IsCountryNotAlias deve essere thread-safe. Sarebbe ancora meglio se è pura funzione.
    2. Nessuno potrà modificare AliasCountryLists nel frattempo, perché i dizionari non sono thread-safe. O utilizzare ConcurrentDictionary per essere sicuri.

    Link utili che vi aiuteranno a:

    Modelli per la Programmazione Parallela: la Comprensione e l’Applicazione di Modelli Paralleli con l’ .NET Framework 4

    Programmazione parallela .Rete 4 Linee guida di Codifica

    Quando Si Dovrebbe Utilizzare In Parallelo.ForEach? Quando devo Usare PLINQ?

    PS: Come si vede nuove caratteristiche parallele non sono così evidenti come si guarda (e si sentono).

  2. 13

    Quando si utilizza AsParallel(), è necessario assicurarsi che il vostro corpo è thread-safe. Purtroppo il codice di cui sopra non funziona. List<T> non è thread-safe, quindi l’aggiunta di AsParallel() causa di una race condition.

    Se, tuttavia, si passa vostre collezioni per l’utilizzo di una raccolta in Sistema.Collezioni.Concorrenti, come ConcurrentBag<T>, il codice di cui sopra, sarà più probabile lavoro.

    • “oltre AsParallel()” come si è fatto nel codice di cui sopra non causare condizione di competizione, perché in c#’s foreach, saranno trattati in modo sequenziale.
    • Vero, sarebbe necessario utilizzare i metodi di estensione su ParallelQuery<T> (o in Parallelo.ForEach invece hanno alcun effetto.
    • Elenco<T> non è threadsafe so , ma so già che plinq divide il lavoro in “blocchi”, e poi raccoglie i risultati ( non come parallelo.foreach – Che avere a che fare il lavoro da me) – in modo che il codice che è threadsafe ? i.imgur.com/mcEwWg2.png , voglio dire, ho sempre di 10 numeri diversi ! così è threadsafe o no ?
  3. 3

    Preferirei usare un’altra struttura di dati come ad esempio un Set per ogni alias e quindi utilizzare unione fonderli.

    Qualcosa di simile a questo

    public string[] ExpandAliases(string[] countries){
        //Alias definitions
        var apac = new HashSet<string> { "US", "FR", ...};
        ... 
    
        var aliases = new HashMap<string, Set<string>> { {"APAC": apac}, ... };
    
        var expanded = new HashSet<string>
        foreach(var country in countries){
            if(aliases.Contains(country)
                expanded.Union(aliases[country]);
            else{
                expanded.Add(country);
        }
    
        return expanded.ToArray();
    }

    Nota: il codice deve essere visto come pseudo-codice.

    • beh, la domanda era PLINQ e Parallelo
    • È anche su speedup e, a volte, il corretto uso delle strutture dati è sufficiente.
  4. 0

    Questo sembra intrinsecamente operazione seriale per me. Tutto quello che stai facendo è un ciclo in una lista di stringhe e di inserirli in un’altra lista. La parallelizzazione librerie intenzione di farlo, più una serie di filettatura e sincronizzazione – si sarebbe probabilmente finire più lento.

    Inoltre, si dovrebbe essere utilizzando un HashSet<string> se non vuoi duplicati.

Lascia un commento