Threading: Gemeinsamer Zugriff auf Date-Objekt

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Hallo,

folgendes Szenario:
ich habe einen Zeitraum über mehrere Tage (beispielsweise 1. Januar bis 31. Januar), den ich abarbeiten möchte. Um das etwas zu beschleunigen will ich da Threads einsetzen.

Ich habe mir jetzt gedacht, dass es eine gemeinsame Variable gibt, die den nächsten zu bearbeiteten Tag anzeigt, und jeder Thread "nimmt" sich praktisch einen Tag daraus und erhöht die Variable um einen Tag.

Tja, nur leider fängt jeder Thread bei dem ersten Tag an. Ich habe das bis jetzt so weit gelöst:

Code: Alles auswählen

class Blabla(threading.Thread):
    lock = threading.Lock()
    next_date = None
    
    def __init__(self, end_date):
        super(Blabla, self).__init__()
        self.end_date = end_date

    def run(self):
        while True:
            self.lock.acquire()
            if self.next_date <= self.end_date:
                current_date = self.next_date
                self.next_date += timedelta(days=1)
            else:
                self.lock.release()
                break
            self.lock.release()
            self.do_something()


Blabla.next_date = date(2015,1,1)
thread1 = Blabla(2015,1,31)
thread2 = Blabla(2015,1,31)
thread1.start()
thread2.start()

Kann mir jemand meinen Denkfehler sagen? Letztlich soll sich halt der schnellste Thread das erste Datum schnappen und es dann erhöhen, bevor es sich der nächste Thread schnappt.

Danke!
Sirius3
User
Beiträge: 17752
Registriert: Sonntag 21. Oktober 2012, 17:20

@Hellstorm: jeder Thread hat bei Dir sein eigenes self.next_date, das unabhängig von allen anderen Threads ist. Normalerweise löst man solche Probleme auch mit einer Queue, in die man mit allen Tagen füllt. Sollte das was Du vorhast, ein rechenintensives Problem sein, werden Dir mehrere Threads aber keine Geschwindigkeit bringen. Dann solltest Du auf multiprocessing umsteigen, das eine map-Funktion hat, die Deine Abarbeitung von Tagen auch eleganter löst.
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Danke für deine Antwort. Ich dachte, wenn ich das nicht in den Konstruktor schreibe, wird die Variable über alle Instanzen der Klasse geteilt. Naja, gut, wieder was gelernt :D

Rechenintensiv ist es nicht unbedingt, aber es soll über das Netzwerk kommunizieren, weswegen das mit den Threads wohl doch etwas schneller gehen sollte (hoffe ich).

Dann schaue ich mir mal die Queue an.

Edit: Ha, super Sache, Queue ist ja unglaublich einfach. Danke!

Code: Alles auswählen

class Blabla(threading.Thread):
    def __init__(self, queue):
        super(Blabla, self).__init__()
        self.queue = queue

    def run(self):
        while not self.queue.empty():
            current_date = self.queue.get()
            self.do_something()

def main():
    q = queue.Queue()
    for dt in rrule(DAILY, dtstart=start, until=until):
        q.put(dt)
    thread1 = Blabla(q)
    thread2 = Blabla(q)
    thread1.start()
    thread2.start()
BlackJack

@Hellstorm: Noch zum ursprünglichen Beitrag: Bei dem Lock kann man sich den Code mit ``with`` einfacher und sicherer machen.

Der neue Code ist nicht thread-sicher. Zwischen dem Test bei der ``while``-Schleife und dem `get()` kann ein anderer Thread bereits das letzte Element aus der Queue geholt haben und das `get()` wird ewig blockieren. Da würde man besser eine ”Endlosschleife” und ein `get_nowait()` verwenden und auf die `Queue.Empty`-Ausnahme entsprechend reagieren.
Hellstorm
User
Beiträge: 231
Registriert: Samstag 22. Juni 2013, 15:01

Ok, ist korrigiert, danke :)
Antworten