Domanda

Ho un ciclo Parallel.ForEach esegue un'operazione intensiva all'interno del corpo.

L'operazione può utilizzare un Hashtable per memorizzare i valori, e può essere riutilizzato per altri elementi di loop consecutivi. Aggiungo al Hashtable dopo l'operazione intensiva è stata completata, la voce ciclo successivo può cercare nella Hashtable e riutilizzare l'oggetto, invece di correre di nuovo l'operazione intensiva.

Tuttavia, perché sto usando Parallel.ForEach c'è un problema pericoloso, causando la Hashtable.Add e il ContainsKey chiamate (chiave) vanno fuori sincrono, in quanto potrebbero essere in esecuzione in parallelo. L'introduzione di serrature può causare problemi di perf.

Ecco il codice di esempio:

Hashtable myTable = new Hashtable;
Parallel.ForEach(items, (item, loopState) =>
{
    // If exists in myTable use it, else add to hashtable
    if(myTable.ContainsKey(item.Key))
    {
       myObj = myTable[item.Key];
    }
    else
    {
       myObj = SomeIntensiveOperation();
       myTable.Add(item.Key, myObj); // Issue is here : breaks with exc during runtime
    }
    // Do something with myObj
    // some code here
}

Ci deve essere un'API, impostazione della proprietà all'interno della libreria TPL, in grado di gestire questo scenario. C'è?

È stato utile?

Soluzione

Siete alla ricerca di System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue> . Le nuove collezioni simultanee utilizzano significativamente migliorati meccanismi di chiusura e dovrebbero effettuare excellectly in algoritmi paralleli.

Modifica: Il risultato potrebbe essere simile a questo:

ConcurrentDictionary<T,K> cache = ...;
Parallel.ForEach(items, (item, loopState) =>
{
    K value;
    if (!cache.TryGetValue(item.Key, out value))
    {
        value = SomeIntensiveOperation();
        cache.TryAdd(item.Key, value);
    }

    // Do something with value
} );

parola di avvertimento: se gli elementi in items non hanno tutti item.Key unico, quindi SomeIntensiveOperation potrebbe ottenere chiamato due volte per quella chiave. Nell'esempio, la chiave non è passata al SomeIntensiveOperation, ma significa che il codice "Fai qualcosa con valore" potrebbe eseguire chiave / valoreA e le coppie chiave / valoreB, e un solo risultato sarebbe ottenere memorizzati nella cache (non necessariamente la prima calcolato SomeIntensiveOperation o). Avreste bisogno di una fabbrica pigra in parallelo per gestire questa se è un problema. Inoltre, per ovvie ragioni SomeIntensiveOperation dovrebbe essere thread-safe.

Altri suggerimenti

Usa un ReaderWriterLock, questo ha buone prestazioni per il lavoro che ha molte letture e scritture pochi che sono di breve durata. Il tuo problema sembra andare bene questa specifica.

Tutte le operazioni di lettura verrà eseguito in modo rapido e bloccare gratuito, l'unica volta che qualcuno viene bloccato è quando una scrittura che sta accadendo, e che in scrittura è solo il tempo che ci vuole per spingere qualcosa in un Hashtable.

ReaderWriterLockSlim su MSDN

Credo che butto giù qualche codice ...

ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();
Hashtable myTable = new Hashtable();
Parallel.ForEach(items, (item, loopState) =>
{
    cacheLock.EnterReadLock();
    MyObject myObj = myTable.TryGet(item.Key);
    cacheLock.ExitReadLock();

    // If the object isn't cached, calculate it and cache it
    if(myObj == null)
    {
       myObj = SomeIntensiveOperation();
       cacheLock.EnterWriteLock();
       try
       {
           myTable.Add(item.Key, myObj);
       }
       finally
       {
           cacheLock.ExitWriteLock();
       }           
    }
    // Do something with myObj
    // some code here
}

static object TryGet(this Hashtable table, object key)
{
    if(table.Contains(key))
        return table[key]
    else
        return null;
}

Non vedo altra scelta corretta che utilizzare (più o meno esplicito) serrature (A Hashtable sincronizzato solo sovrascrive tutti i metodi con serrature).

Un'altra opzione potrebbe essere quella di consentire il dizionario di andare fuori sincrono. La condizione di gara non corrompere il dizionario, sarà solo richiedere il codice per fare alcuni calcoli superflui. Profilo il codice per verificare se il blocco o mancante Memoizzazione ha effetti peggiori.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top