2015-06-04 3 views
6

Nehmen wir an, ich das folgende Stück Code haben:LINQ auf Schleifenbedingungen

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

Wie Sie sehen können, bin ich das Abrufen alle Schlüssel in _cache, sie in meinem lokalen Variablen speichern allKeys, und dann gleichzeitig Entfernen aller Schlüssel von _cache.

Ich möchte jedoch, es eine einzige Zeile zu tun. Also, was in den Sinn kommt ist:

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

Aber die Aussage _cache.Select(o => o.Key) würde auf jeder Schleife Iteration aufgerufen werden, damit unterschiedliche Menge von Elementen jedes Mal Abrufen (weil ich sie zur gleichen Zeit bin zu löschen).

Ist die letzte Codezeile sicher?

Wird _cache.Select(o => o.Key) in Schleifenanweisungen nur einmal aufgerufen, und dann verwendet jede Iteration das ursprüngliche Ergebnis oder wird es in jedem Iterationsschritt verarbeitet?

+1

Welcher Typ ist _cache? – Paddy

+3

Was wäre der Vorteil, dies in einer einzigen Zeile zu tun? –

+2

_ "Aber die Anweisung _cache.Select (o => o.Key) würde bei jeder Schleifeniteration aufgerufen werden" _ - bist du sicher? – CodeCaster

Antwort

4

Zunächst sind beide Codes gleich. Es gibt keinen Unterschied, ob Sie eine temporäre Variable haben oder nicht.

Zweitens: Dieser Code ist fehlerhaft.

  1. LINQ verwendet die verzögerte Ausführung. Mit anderen Worten, beim IterierenallKeys, die zugrunde liegenden Daten - in Ihrem Fall _cache - wird iteriert. In Kombination mit dem Entfernen funktioniert dies nicht.
  2. _cache höchstwahrscheinlich ist ein normales Wörterbuch oder etwas ähnliches. Mit anderen Worten, es ist nicht threadsicher. UPDATE: Laut einem Kommentar ist es vom Typ ObjectCache und dieser Typ ist tatsächlich Thread-sicher. Dieses Problem tritt in Ihrem speziellen Fall nicht auf.
+0

Der erste Punkt hängt von der Datenstruktur ab. Es ist sicherlich * möglich * (aber unwahrscheinlich), eine Datenstruktur zu haben, die dies unterstützt. Letzteres ist der eigentliche Deal Breaker. Im besten Fall könnte die Datenstruktur die Entfernungen serialisieren und so sicher halten; Es gibt praktisch keine Möglichkeit, parallel zu arbeiten. – Servy

4

Wie Sie sehen können, bin ich das Abrufen alle Schlüssel in _cache, sie in meinem lokalen Variablen AllKeys

Keine Speicherung, Sie nicht. Aufgrund einer sogenannten verzögerten Ausführung speichern Sie nur den Befehl, um alle Ihre Schlüssel zu erhalten. Sie müssen diesen Befehl materialisieren, um tatsächlich das tun, was Sie denken, Sie tun:

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

Das sagte: Ist Ihr Cache Thread sicher? Warum bietet es keine Clear-Methode? Es ist eine nicht ganz so gute Idee, alle Schlüssel zu bekommen und sie mit Multi-Threading zu entfernen.

Wenn Sie das alles in einer Zeile zu haben, darauf bestehen, könnten Sie PLINQ verwenden:

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

Aber noch einmal: Das scheint eine schlechte Idee zu sein.

1

Wäre es nicht effizienter zu Dispose von Ihrem vorhandenen _cache-Objekt und nur neu erstellen, anstatt jedes Element einzeln zu entfernen?

Speichert die Abfrage und Looping ...

+0

Ich dachte, es zu beseitigen, aber mein Cache ist vom Typ 'MemoryCache', deshalb wird es nicht gemäß diesem Beitrag empfohlen: [Wie lösche ich ein System.Runtime.Caching.MemoryCache?] (Http://stackoverflow.com/ Fragen/8043381/how-do-i-klar-ein-System-Laufzeit-Caching-Speichercache) –

+0

OK, interessant, aber möglicherweise (wie auch in den Antworten dort erwähnt), nur _Cache zu setzen, um eine neue Instanz würde ausreichen? – Paddy

0

First off, können Sie nicht eine Sammlung ändern, während über sie iterieren, das ist, was .Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) hinter den Kulissen tut. Also diese Linie

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

wird einfach nicht funktionieren.

Sie könnten versuchen, diese

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

, die auf einer Kopie der Tasten funktionieren. Der ObjectCache Typ ist threadsicher, ebenso wie MemoryCache, also sollten Sie in Ordnung sein.

Das einzige mögliche Problem hier ist, wenn dieser Code in einer Multithread-Anwendung (wie eine Webanwendung) ist. Mehrere Threads zu haben, die in der Lage sind, aus dem Cache zu lesen/schreiben/zu löschen, eröffnet eine riesige Anzahl von Würmern und macht die Verwendung von Sperren zur Verwaltung des Cache-Zugriffs erforderlich.