2010-11-18 6 views
5

Ich bin nach einem threadsicheren Python-Container, wo die Werte nach einiger Zeit automatisch entfernt werden. Existiert eine solche Klasse?Container, wo Werte in Python ablaufen

+3

möglich duplizieren: http://stackoverflow.com/questions/3927166/automatisch-expiring-variable – mouad

+0

Ich bin nach einem Thread s eine Klasse mit Timeouts für jeden Wert. In diesem Beispiel wird eine Liste und ein globales Timeout verwendet. – hoju

Antwort

5

Hier ist ein Thread sichere Version von ExpireCounter:

import datetime 
import collections 
import threading 

class ExpireCounter: 
    """Tracks how many events were added in the preceding time period 
    """ 

    def __init__(self, timeout=1): 
     self.lock=threading.Lock()   
     self.timeout = timeout 
     self.events = collections.deque() 

    def add(self,item): 
     """Add event time 
     """ 
     with self.lock: 
      self.events.append(item) 
      threading.Timer(self.timeout,self.expire).start() 

    def __len__(self): 
     """Return number of active events 
     """ 
     with self.lock: 
      return len(self.events) 

    def expire(self): 
     """Remove any expired events 
     """ 
     with self.lock: 
      self.events.popleft() 

    def __str__(self): 
     with self.lock: 
      return str(self.events) 

, die wie folgt verwendet werden kann:

import time 
c = ExpireCounter() 
assert(len(c) == 0) 
print(c) 
# deque([]) 

c.add(datetime.datetime.now()) 
time.sleep(0.75) 
c.add(datetime.datetime.now())  
assert(len(c) == 2) 
print(c) 
# deque([datetime.datetime(2010, 11, 19, 8, 50, 0, 91426), datetime.datetime(2010, 11, 19, 8, 50, 0, 842715)]) 

time.sleep(0.75) 
assert(len(c) == 1) 
print(c) 
# deque([datetime.datetime(2010, 11, 19, 8, 50, 0, 842715)]) 
+0

danke! Ich mag die Thread-Zeitüberschreitung. War mein Beispiel nicht threadsicher? Die Dokumentation sagt "Deque ist eine alternative Implementierung von unbegrenzten Warteschlangen mit schnellen atomaren append() - und popleft() -Operationen, die nicht gesperrt werden müssen." – hoju

+0

@Plumo: Ich bin kein Experte in der Beurteilung der Thread-Sicherheit, aber ich denke, Ihre Version von ExpireCounter ist möglicherweise nicht Thread-sicher. In der 'add'-Methode kann auf den Aufruf von' datetime.now() 'nicht sofort der Aufruf von' self.events.append' folgen. Stellen Sie sich mehrere Threads vor, die die add-Methode fast gleichzeitig aufrufen. Viele Aufrufe von "datetime.now", aber die Ergebnisse werden an "self.events" in einer uneinheitlichen Reihenfolge angehängt. Wenn 'self.events' nicht chronologisch geordnet ist, endet die while-Schleife in der' expire'-Methode möglicherweise zu früh. Daher kann es passieren, dass alle Elemente, die das Zeitlimit überschritten haben, nicht "verloren" werden. – unutbu

1

Vielleicht möchten Sie einen LRU-Cache. Hier ist ein Ich habe Sinn versuchen:

http://pypi.python.org/pypi/repoze.lru

Es Thread-sicher zu sein scheint.

+0

nein nicht LRU. Ich möchte, dass ein Wert nach genau dem angegebenen Timeout abläuft, unabhängig davon, wie viele Werte ich habe und ob ich darauf zugreife. – hoju

+2

In diesem Fall könnten Sie mit jedem Wert eine Ablaufzeit speichern. Welche Art von Containersemantik möchten Sie verwenden: Liste, Satz, Diktat oder etwas anderes? –

+0

nicht mit Art des Behälters betroffen, solange es Thread-sicher ist – hoju

0

Dies ist mehr oder weniger das, was ich jetzt will:

from datetime import datetime, timedelta 
from collections import deque 

class ExpireCounter: 
    """Tracks how many events were added in the preceding time period 
    """ 

    def __init__(self, timeout=timedelta(seconds=1)): 
     self.timeout = timeout 
     self.events = deque() 

    def add(self): 
     """Add event time 
     """ 
     self.events.append(datetime.now()) 

    def __len__(self): 
     """Return number of active events 
     """ 
     self.expire() 
     return len(self.events) 

    def expire(self): 
     """Remove any expired events 
     """ 
     now = datetime.now() 
     try: 
      while self.events[0] + self.timeout < now: 
       self.events.popleft() 
     except IndexError: 
      pass # no more events 


if __name__ == '__main__': 
    import time 
    c = ExpireCounter() 
    assert(len(c) == 0) 
    c.inc() 
    time.sleep(0.75) 
    c.inc() 
    assert(len(c) == 2) 
    time.sleep(0.75) 
    assert(len(c) == 1)