2015-06-04 15 views
6

Supponiamo Ho il seguente pezzo di codice:LINQ in condizioni di loop

IEnumerable<string> allKeys = _cache.Select(o => o.Key); 
Parallel.ForEach(allKeys, key => _cache.Remove(key)); 

Come potete vedere, sto recuperando tutte le chiavi _cache, la loro memorizzazione nella mia variabile locale allKeys, e quindi in concomitanza rimuovendo tutte le chiavi da _cache.

Vorrei, tuttavia, farlo un'unica riga. Quindi, quello che viene in mente è:

Parallel.ForEach(_cache.Select(o => o.Key), key => _cache.Remove(key)); 

Ma la dichiarazione _cache.Select(o => o.Key) sarebbe chiamato su ogni iterazione del ciclo, quindi, il recupero di diverse quantità di elementi ogni volta (perché io li sto eliminando allo stesso tempo).

L'ultima riga di codice è sicura?

Nelle istruzioni loop, _cache.Select(o => o.Key), viene richiamato una sola volta, quindi ogni iterazione utilizza il risultato originale o viene elaborato in ogni fase di iterazione?

+1

Che tipo è _cache? – Paddy

+3

Quale sarebbe il vantaggio di farlo in una singola riga? –

+2

_ "Ma l'istruzione _cache.Select (o => o.Key) verrebbe chiamata su ogni ciclo iterativo" _ - sei sicuro? – CodeCaster

risposta

4

Prima di tutto, entrambi i codici sono uguali. Non c'è differenza se hai una variabile temporanea o no.

Secondo: questo codice è difettoso.

  1. LINQ utilizza l'esecuzione posticipata. In altre parole, durante l'iterazione diallKeys, i dati sottostanti - nel tuo caso _cache - vengono iterati. In combinazione con la rimozione, questo non funzionerà.
  2. _cache molto probabilmente è un dizionario normale o qualcosa di simile. In altre parole, non è thread-safe. AGGIORNAMENTO: in base ad un commento, è di tipo ObjectCache e tale tipo è effettivamente sicuro per i thread. Quindi questo problema non si verifica nel tuo caso specifico.
+0

Il primo punto dipenderà dalla struttura dei dati. È certamente * possibile * (ma improbabile) avere una struttura dati che lo supporti. Quest'ultimo è il vero affare. Nel migliore dei casi, la struttura dei dati potrebbe serializzare le rimozioni, mantenendole sicure; non c'è proprio modo per farlo funzionare in parallelo. – Servy

4

Come potete vedere, sto recuperando tutte le chiavi _cache, la loro memorizzazione nei miei allKeys variabili locali

No, non lo fai. A causa di una cosa chiamata esecuzione differita, tutto ciò che memorizzi è il comando per ottenere tutte le tue chiavi. È necessario per materializzare questo comando per effettivamente fare ciò che si pensa di fare:

var = allKeys _cache.Select (o => o.Key) ToList();

Detto questo: il thread della cache è sicuro? Perché non presenta un metodo Clear? Ottenere tutte le chiavi e rimuoverlo usando il multi-threading sembra un'idea non così eccezionale.

Se si insiste per avere tutto in una sola riga, è possibile utilizzare PLINQ:

_cache.Select(o => o.Key).AsParallel().ForAll(key => _cache.Remove(key)); 

Ma ancora una volta: questa sembra essere una cattiva idea.

1

Non sarebbe più efficiente di Dispose dell'oggetto _cache esistente e semplicemente ricrearlo, anziché rimuovere ogni elemento singolarmente?

Più Siti interrogazione e loop ...

+0

Ho pensato di eliminarlo, ma la mia cache è di tipo 'MemoryCache', quindi non è raccomandato in base a questo post: [Come faccio a cancellare System.Runtime.Caching.MemoryCache?] (Http://stackoverflow.com/ domande/8043381/how-do-i-clear-a-system-runtime-cache-memorycache) –

+0

OK, interessante, ma probabilmente (come indicato anche nelle risposte), basterà impostare _cache come nuova istanza sarebbe sufficiente? – Paddy

0

Prima di tutto, non è possibile modificare una raccolta, mentre l'iterazione su di esso, che è quello che .Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) fa dietro le quinte. Quindi questa riga

Parallel.ForEach(_cache.Select(o => o.Key), key => _cache.Remove(key)); 

semplicemente non funziona.

Si potrebbe provare questo

Parallel.ForEach(_cache.Select(o => o.Key).ToList(), key => _cache.Remove(key)); 

che lavorerà su una copia delle chiavi. Il tipo ObjectCache è thread-safe, così come lo è MemoryCache quindi dovresti stare bene.

L'unico problema potenziale qui è se questo codice si trova in un'applicazione multithread (come un'applicazione Web). Avere più thread in grado di leggere/scrivere/eliminare nella/dalla cache apre una grande quantità di worm e rende obbligatorio l'uso di lock per gestire l'accesso alla cache.