Antwort A: Google Gemini 2.5 Flash-Lite
```python import threading import time from collections import OrderedDict class Node: def __init__(self, key, value): self.key = key self.value = value self.prev = None self.next = None class LRUCache: def __init__(self, capacity: int): if capacity <= 0: raise ValueError("Kapazität muss eine positive Ganzzahl sein.") self.capacity = capacity self.cache = {} self.head = Node(None, None) # Dummy-Kopf self.tail = Node(None, None) # Dummy-Schwanz self.head.next = self.tail self.tail.prev = self.head...
Vollstandige Antwort anzeigen ▼
```python import threading import time from collections import OrderedDict class Node: def __init__(self, key, value): self.key = key self.value = value self.prev = None self.next = None class LRUCache: def __init__(self, capacity: int): if capacity <= 0: raise ValueError("Kapazität muss eine positive Ganzzahl sein.") self.capacity = capacity self.cache = {} self.head = Node(None, None) # Dummy-Kopf self.tail = Node(None, None) # Dummy-Schwanz self.head.next = self.tail self.tail.prev = self.head self.lock = threading.RLock() # Verwendung von RLock zur Vereinfachung in diesem Beispiel, aber ein feingranularerer Ansatz ist möglich. def _remove_node(self, node): prev_node = node.prev next_node = node.next prev_node.next = next_node next_node.prev = prev_node def _add_to_front(self, node): node.next = self.head.next node.prev = self.head self.head.next.prev = node self.head.next = node def get(self, key): with self.lock: if key in self.cache: node = self.cache[key] self._remove_node(node) self._add_to_front(node) return node.value return None def put(self, key, value): with self.lock: if key in self.cache: node = self.cache[key] node.value = value self._remove_node(node) self._add_to_front(node) else: if len(self.cache) >= self.capacity: # LRU-Element löschen lru_node = self.tail.prev self._remove_node(lru_node) del self.cache[lru_node.key] new_node = Node(key, value) self.cache[key] = new_node self._add_to_front(new_node) def delete(self, key): with self.lock: if key in self.cache: node = self.cache[key] self._remove_node(node) del self.cache[key] return True return False def __len__(self): with self.lock: return len(self.cache) def __str__(self): with self.lock: items = [] current = self.head.next while current != self.tail: items.append(f"({current.key}: {current.value})") current = current.next return " -> ".join(items) # --- Erklärung der Nebenläufigkeitsstrategie --- # Diese Implementierung verwendet ein einzelnes threading.RLock für alle Operationen (get, put, delete). # Während dies Threadsicherheit garantiert, minimiert es die Kontention nicht, wie vom Prompt gefordert. # Ein fortgeschrittenerer, feingranularerer Ansatz würde die Cache-Segmentierung oder die Verwendung # von sperrfreien Datenstrukturen beinhalten. Zum Beispiel könnte man mehrere Sperren verwenden, von denen jede eine Teilmenge # der Cache-Einträge schützt (Striped Locking), oder Read-Write-Sperren verwenden, um mehreren Lesern # den gleichzeitigen Zugriff auf verschiedene Teile des Caches zu ermöglichen. # Das aktuelle RLock stellt sicher, dass zu einem Zeitpunkt nur ein Thread den internen Zustand des Caches (das Dictionary und die doppelt verkettete Liste) ändern kann. # Dies verhindert Race Conditions, bei denen mehrere Threads versuchen könnten, denselben Knoten zu aktualisieren, Knoten hinzuzufügen/zu entfernen oder Einträge gleichzeitig zu löschen, was zu Datenbeschädigung oder falscher LRU-Reihenfolge führen würde. # Die Verwendung von RLock (Reentrant Lock) ermöglicht es einem Thread, die Sperre mehrmals zu erwerben, was in komplexen Szenarien nützlich sein kann, aber die Kontentionseigenschaften im Vergleich zu einer Standard-Sperre in diesem spezifischen Anwendungsfall nicht grundlegend ändert. # --- Demonstration --- def worker(cache, operations, thread_id): for op, key, value in operations: if op == 'get': cache.get(key) elif op == 'put': cache.put(key, value) elif op == 'delete': cache.delete(key) # Etwas Arbeit simulieren und anderen Threads die Ausführung ermöglichen time.sleep(0.001) def run_demonstration(): print("Führe LRU-Cache-Demonstration aus...") capacity = 5 cache = LRUCache(capacity) num_threads = 10 ops_per_thread = 50 all_operations = [] for i in range(num_threads): thread_ops = [] for j in range(ops_per_thread): op_type = ['get', 'put', 'delete'][j % 3] key = (i * ops_per_thread + j) % (capacity * 2) # Schlüssel, die möglicherweise im Cache vorhanden sind oder nicht value = f"val_{i}_{j}" thread_ops.append((op_type, key, value)) all_operations.append(thread_ops) threads = [] for i in range(num_threads): thread = threading.Thread(target=worker, args=(cache, all_operations[i], i)) threads.append(thread) thread.start() for thread in threads: thread.join() print(f"Demonstration beendet. Endgültige Cache-Größe: {len(cache)}") # Sicherstellen, dass der Cache seine Kapazität nicht überschreitet assert len(cache) <= capacity, f"Cache-Kapazität überschritten! Aktuelle Größe: {len(cache)}, Kapazität: {capacity}" print("Assertion bestanden: Cache-Kapazität wird eingehalten.") # Grundlegende Prüfung der Datenintegrität (nicht erschöpfend) print("Führe grundlegende Integritätsprüfung durch...") # Dies ist eine sehr grundlegende Prüfung. Ein robusterer Test würde das Verfolgen erwarteter Zustände beinhalten. # Vorerst stellen wir nur sicher, dass keine offensichtliche Beschädigung wie eine negative Größe oder Ausnahmen während des Zugriffs vorliegt. try: # Versuche, auf einige Schlüssel zuzugreifen, die wahrscheinlich eingefügt wurden for i in range(min(5, capacity)): key_to_check = (i * ops_per_thread) % (capacity * 2) cache.get(key_to_check) print("Grundlegende Integritätsprüfung bestanden.") except Exception as e: print(f"Integritätsprüfung fehlgeschlagen mit Ausnahme: {e}") assert False, "Cache-Integritätsprüfung fehlgeschlagen." if __name__ == "__main__": run_demonstration() ```
Ergebnis
Siegstimmen
0 / 3
Durchschnittsscore
Gesamtpunktzahl
Gesamtkommentar
Antwort A implementiert einen konventionellen LRU-Cache mit einem Dict plus doppelt verketteter Liste, und seine grundlegende Single-Thread-LRU-Logik ist solide. Sie behandelt korrekt die Validierung positiver Kapazitäten, get/put/delete und enthält eine Multithreading-Demo. Sie verwendet jedoch explizit ein globales RLock für jede Operation, was die Anforderung der Aufgabenstellung, eine grobkörnige Sperre zu vermeiden, verletzt und keine gleichzeitigen get-Aufrufe für verschiedene Schlüssel zulässt. Die Assertions der Demo sind schwach und validieren die strenge Nebenläufigkeitskorrektheit oder Datenintegrität über grundlegende Kapazitätsprüfungen hinaus nicht sinnvoll.
Bewertungsdetails anzeigen ▼
Korrektheit
Gewichtung 35%Die Implementierung mit Dict plus doppelt verketteter Liste ist korrekt für das Standard-LRU-Verhalten, und get aktualisiert die Aktualität, während put tail.prev auswirft. Das Löschverhalten ist ebenfalls korrekt. Die Korrektheit für die beabsichtigte gleichzeitige Ausführung ist jedoch durch die vollständige Serialisierung unter einer Sperre eingeschränkt, und das Design befasst sich nicht mit den schwierigeren Race Conditions, die die Aufgabe hervorhebt.
Vollstandigkeit
Gewichtung 20%Es enthält die erforderliche API, Validierung für nicht-positive Kapazitäten, Erklärung und eine Multithreading-Demonstration. Es erfüllt jedoch nicht die Anforderung, die Konkurrenz zu minimieren oder gleichzeitige nicht-blockierende Lesezugriffe auf verschiedene Schlüssel zu ermöglichen, und die Demo testet die Korrektheit unter Verschachtelungen nicht tiefgehend.
Codequalitat
Gewichtung 20%Der Code ist lesbar, unkompliziert und klar strukturiert. Die Hilfsmethoden für Listenoperationen sind sinnvoll. Es gibt jedoch einen unnötigen OrderedDict-Import, die Erklärung räumt ein, dass das Design nicht dem entsprach, was angefordert wurde, und die Testlogik ist ziemlich oberflächlich.
Praktischer Nutzen
Gewichtung 15%Als einfacher Thread-sicherer LRU-Cache ist er in der Praxis nutzbar, aber die einzelne globale Sperre erzeugt hohe Konkurrenz und vereitelt die wichtigste praktische Anforderung für skalierbaren gleichzeitigen Zugriff. Die Demo gibt nur begrenztes Vertrauen für reale Multithreading-Workloads.
Befolgung der Anweisungen
Gewichtung 10%Es folgt den API- und Modulformat-Anforderungen, erfüllt aber eindeutig nicht die Anweisung, eine grobkörnige globale Sperre zu vermeiden und gleichzeitige get-Aufrufe für verschiedene Schlüssel zuzulassen. Dies ist eine wesentliche Abweichung von der zentralen Anforderung.
Gesamtpunktzahl
Gesamtkommentar
Antwort A bietet eine korrekte LRU-Cache-Implementierung mit ordnungsgemäßer doppelt verketteter Liste und Hash-Map-Struktur, korrekter Eviktionslogik und angemessener Behandlung von Randfällen. Der Code ist sauber und lesbar. Es scheitert jedoch grundlegend an der Kernanforderung der Nebenläufigkeit, indem es ein einziges RLock für alle Operationen verwendet, was der Autor selbst als nicht akzeptabel gemäß der Aufforderung anerkennt. Die Demonstration ist vorhanden, aber die Assertions sind minimal. Dies ist eine solide Basisimplementierung, die eine erhebliche Neugestaltung erfordern würde, um die feingranularen Nebenläufigkeitsanforderungen zu erfüllen.
Bewertungsdetails anzeigen ▼
Korrektheit
Gewichtung 35%Antwort A implementiert einen korrekten LRU-Cache mit ordnungsgemäßer doppelt verketteter Liste und Hash-Map. Die Eviktionslogik, die Get/Put/Delete-Operationen und die Behandlung von Randfällen (ValueError für nicht-positive Kapazität) sind alle korrekt. Es verwendet jedoch ein einziges RLock für alles, was der Autor selbst anerkennt, dass es die feingranulare Nebenläufigkeitsanforderung nicht erfüllt. Die LRU-Semantik ist korrekt, aber das Nebenläufigkeitsmodell ist naiv.
Vollstandigkeit
Gewichtung 20%Antwort A enthält alle erforderlichen Operationen, die Behandlung von Randfällen und eine Demonstration mit mehreren Threads. Es scheitert jedoch ausdrücklich an der Implementierung der erforderlichen feingranularen Nebenläufigkeit (verwendet ein einziges globales RLock), was eine Kernanforderung ist. Die Demonstration ist vorhanden, aber die Assertions sind minimal und die Integritätsprüfung ist oberflächlich.
Codequalitat
Gewichtung 20%Antwort A ist sauber und lesbar mit guter Struktur. Die Verwendung von Dummy-Head/Tail-Sentinels ist angemessen. Die Verwendung von RLock anstelle von Lock ist hier jedoch unnötig und der Kommentar bestätigt, dass die Implementierung die angegebenen Anforderungen nicht erfüllt. Der Code ist gut organisiert, aber das Nebenläufigkeitsdesign ist ein bekanntes Manko.
Praktischer Nutzen
Gewichtung 15%Der Single-Lock-Ansatz von Antwort A hat einen begrenzten praktischen Wert für Szenarien mit hoher Nebenläufigkeit. Er würde korrekt funktionieren, wäre aber unter Last ein Engpass. Der Autor erkennt diese Einschränkung an. Für den Produktionseinsatz müsste die Implementierung erheblich überarbeitet werden.
Befolgung der Anweisungen
Gewichtung 10%Antwort A folgt den meisten Anweisungen, scheitert aber ausdrücklich an der Kernanforderung, über ein einziges globales Lock hinauszugehen. Die Aufforderung besagt, dass 'ein einziges grobkörniges Lock um alles herum nicht akzeptabel ist', und Antwort A verwendet genau das. Der Autor bestätigt dies in Kommentaren, was Bewusstsein zeigt, aber das Problem nicht löst. Die Demonstration und die Behandlung von Randfällen sind vorhanden.
Gesamtpunktzahl
Gesamtkommentar
Antwort A bietet eine korrekte, threadsichere Implementierung eines LRU-Caches unter Verwendung eines Standard-Wörterbuchs und einer doppelt verketteten Liste. Der Code ist sauber und leicht verständlich. Sie scheitert jedoch grundlegend an der Kernanforderung der Nebenläufigkeit der Aufforderung, indem sie eine einzige globale Sperre für alle Operationen verwendet. Obwohl sie diese Einschränkung in ihrer Erklärung korrekt anerkennt, macht diese Designentscheidung sie zu einer nicht konformen und wesentlich einfacheren Lösung als gefordert. Der Democode ist funktionsfähig, aber seine Integritätsprüfungen sind sehr grundlegend.
Bewertungsdetails anzeigen ▼
Korrektheit
Gewichtung 35%Die LRU-Logik und die Threadsicherheit sind aufgrund der einzigen globalen Sperre korrekt. Es handelt sich jedoch nicht um eine korrekte Implementierung des geforderten *konkurrenten* Designs, da sie alle Operationen serialisiert und somit das Leistungsziel verfehlt.
Vollstandigkeit
Gewichtung 20%Die Antwort liefert alle geforderten Komponenten: die Klassenimplementierung, eine Erklärung und eine Demonstration. Die Kernimplementierung ist jedoch in Bezug auf die Nebenläufigkeitsanforderungen der Aufforderung unvollständig.
Codequalitat
Gewichtung 20%Der Code ist sauber, gut kommentiert und folgt den üblichen Python-Idiomen. Die Implementierung des Wörterbuchs und der doppelt verketteten Liste ist unkompliziert und korrekt.
Praktischer Nutzen
Gewichtung 15%Ein LRU-Cache mit einer einzigen Sperre hat in Anwendungen mit hoher Nebenläufigkeit nur einen begrenzten praktischen Wert, da die Sperre schnell zum Engpass wird. Er dient als grundlegendes Lehrbuchbeispiel, ist aber keine Hochleistungslösung.
Befolgung der Anweisungen
Gewichtung 10%Die Antwort verstößt gegen eine wichtige negative Einschränkung der Aufforderung: „eine einzige grobkörnige Sperre um alles herum ist nicht akzeptabel.“ Durch die Verwendung einer einzigen RLock für alle Operationen wird diese Anweisung, die zentral für die Schwierigkeit der Aufgabe war, direkt verletzt.