2010-02-01 11 views
6

Diese Frage bezieht sich auf Leistungseinbußen, die sich aus einer großen Anzahl schlafender Python-Threads auf einem Webserver ergeben können.Python: Strafe für schlafende Threads

Hintergrund: Ich implementiere einen Onlineshop mit django/satchmo. Voraussetzung ist eine verspätete Zahlung. Der Kunde kann ein Produkt reservieren und zu einem späteren Zeitpunkt von einem Dritten bezahlen lassen (über eine zufällige und eindeutige URL).

Um einen Artikel zu reservieren, erstelle ich einen Thread, der für die Reservierungszeit schläft und dann die Reservierung entfernt/markiert das Produkt als verkauft, wenn es erwacht. Es sieht wie folgt aus:

#Reserves a product when it is placed in the cart 
def reserve_cart_product(product): 
    log.debug("Reserving %s" % product.name) 
    product.active = False 
    product.featured = False 
    product.save() 
    from threading import Timer 
    Timer(CART_RESERVE_TIME, check_reservation, (product,)).start() 

ich die gleiche Technik verwende, wenn die eindeutige URLs Culling, nachdem sie abgelaufen sind, wird nur der Timer für viel länger schläft (in der Regel 5 Tage).

Also, um Ihnen meine Frage ist so wie folgt:

eine große numnber Schlaf Fäden gehen ernsthaft Leistung Wirkung zeigt? Gibt es bessere Techniken, um eine einmalige Veranstaltung in der Zukunft zu planen? Ich würde dies gerne in Python behalten, wenn möglich; Kein Anruf at oder cron über sys.

Die Website ist nicht gerade stark frequentiert; Eine (großzügige) Obergrenze für Produkte, die pro Woche bestellt werden, liegt bei etwa 100. In Kombination mit einer Wagenreservierung könnte dies bedeuten, dass mehr als 100 Schlaffäden gleichzeitig vorhanden sind. Werde ich es bereuen, Aufgaben auf diese Weise zu planen?

Danke

+1

Sie möchten vielleicht eine beständigere Lösung als Threads für den Fall, dass Ihr Server ausfällt. Soweit ich das beurteilen kann, müssen Sie Ihre Log-Datei durchsuchen, um zu erfahren, welche Produkte nach einem Absturz reserviert wurden (obwohl Sie nicht wissen, wie lange sie mit dem obigen Code reserviert waren). – tgray

+0

Sie machen einen guten Punkt und aus diesem Grund habe ich begonnen, einige Datensätze in der DB zu speichern. – pisswillis

+0

Sie gehen davon aus, dass Ihr Server nicht neu gestartet wird, und Sie werden nicht Tausende von Bestellungen erhalten, richtig? Eine robustere Option wäre ein persistentes Datenbankwarteschlangensystem wie RabbitMQ. –

Antwort

7

Ich sehe keinen Grund, warum das nicht funktionieren sollte. Der zugrunde liegende Code für Timer (in threading.py) verwendet einfach time.sleep. Wenn es eine Weile gewartet hat, wird im Prinzip eine Schleife mit time.sleep (0,05) ausgeführt. Dies sollte zu einer CPU-Auslastung von grundsätzlich 0% führen, selbst bei Hunderten von Threads. Hier ist ein einfaches Beispiel, wo ich 0% CPU-Auslastung für den Python-Prozess bemerkt:

import threading 

def nothing(): 
    pass 

def testThreads(): 
    timers = [threading.Timer(10.0, nothing) for _ in xrange(881)] 
    print "Starting threads." 
    map(threading.Thread.start, timers) 
    print "Joining threads." 
    map(threading.Thread.join, timers) 
    print "Done." 

if __name__ == "__main__": 
    testThreads() 

Das eigentliche Problem ist, dass Sie nicht in der Lage sein können, um tatsächlich zu viele Threads zu starten. Auf meinem 64-Bit-4-GB-System kann ich nur 881 Threads starten, bevor ein Fehler auftritt. Wenn Sie wirklich nur ein paar hundert haben werden, kann ich mir nicht vorstellen, dass es nicht funktionieren wird.

3

Normalerweise haben schlafende Threads abgesehen von dem Speicher, der für ihre Stacks und andere private Daten reserviert ist, keinen Overhead. Modern Operating System Scheduling-Algorithmen haben die Komplexität O (1), so dass selbst ein laufender Thread keinen Overhead mit sich bringt, abgesehen vom Speicherbedarf. Gleichzeitig ist es schwierig, sich ein effizientes Design vorzustellen, das viele Gewinde erfordert. Den einzigen Fall, den ich mir vorstellen kann, ist die Kommunikation mit vielen anderen Kollegen. In diesem Fall sollte asynchrones IO verwendet werden.

4

100 Threads ist kein Problem, aber wie tgray pointed out, was passiert, wenn der Server ausfällt (Stromausfall, geplante Wartung, Hardwarefehler, etc.)?

Sie müssen die Unersparungsinformationen irgendwo in Ihrer Datenbank speichern.

Dann können Sie einen Cron-Job in regelmäßigen Abständen zum Beispiel ein Unserservationsskript auslösen lassen, und Sie müssen nicht alle diese Threads herumstehen haben.

Wenn Sie wirklich nicht Cron verwenden möchten, haben Sie nur einen Worker Thread, der eine Minute lang schläft und dann prüft, ob irgendwelche Unerwartetes fällig ist.