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.
Hallo,
brauche ich einen Lock für den Counter finishedItems? Habe fünf Worker Threads, die sich Elemente aus einer Queue holen. Der Counter gibt an wieviele Elemente bearbeitet wurden. Ein Logger Thread gibt alle 5 Sekunden diese Variable. Ich bin der Meinung, dass ich keinen Lock angeben muss, oder?
finishedItems +=1 und print("Finshed items: "+str(finishedItems)) sind für mich atomar und können nicht zwischendrin unterbrochen werden. Oder sehe ich das falsch?
import queue
import threading
import time
def worker():
global finishedItems
while(True):
# do something with the element of the queue q
try:
element=q.get(False)
except queue.Empty:
break
finishedItems +=1
time.sleep(1)
def log():
global finishedItems
while(True):
time.sleep(5)
print("Finshed items: "+str(finishedItems))
if q.empty():
break
finishedItems=0
q=queue.Queue()
for i in range(100000):
q.put(i)
for i in range(5):
x=threading.Thread(target=worker)
x.start()
logThread = threading.Thread(target=log )
logThread.start()
logThread.join()
Bevor Du anfängst, mit Threads zu arbeiten, solltest Du erst einmal lernen, wie man sauber Funktionen schreibt. Schon in Deinem anderen Thread, habe ich geschrieben, dass alles was eine Funktion braucht, über ihre Argumente kommen muß. Jetzt fängst Du auch noch mit `global` an, was man nie und bei Multithreading erst recht nie benutzen sollte.
Schreib erst einmal Dein Programm so um, dass es saubere Funktionen und eine main-Funktion hat und dann kann man sich überlegen, wie man mit einem finished_items (Variablennamen scheibt man komplett klein) umgeht. Dabei nicht vergessen, dass immer mit 4 Leerzeichen pro Ebene eingerückt wird und auf keinen Fall Tabs mit Leerzeichen gemischt werden sollten.
-Eingerückt wird mit 4 Leerzeichen
-Variablennamen sollten aussagekräftig sein
-Variablen werden laut Konvention klein_mit_unterstrich geschrieben
-Strings setzt man nicht mit + zusammen. Dafür gibt es Stringformatierung.
-Es sollte kein Code (Bis auf Konstanten) auf Modulebene stehen. Pack das einfach in eine main() Funktion.
Danke für das Feedback. Habe den Code überarbeitet.
Bei der Übergabe von Parametern an Funktionen war ich mir nicht sicher. Ausschlaggebend war, dass in der Python-Doku in https://docs.python.org/3/library/queue.html(siehe unter join) von der Funktion worker auf eine globale Queue zugegriffen wird. Und wenn die das so machen, dann sollte das richtig sein. Desweiteren habe ich das auch in einigen anderen Tutorials gesehen. Von C/C++ weiß ich, dass man sowas nicht machen soll.
import queue
import threading
import time
def worker(queue,lock):
global finished_items
while True:
# do something with the element of the queue q
try:
lock.acquire()
element=queue.get(False)
lock.release()
except queue.Empty:
break
finally:
lock.release()
finished_items +=1
time.sleep(1)
def log(queue):
global finished_items
while True:
time.sleep(5)
print("Finished items: %s" % str(finished_items))
if queue.empty():
break
if __name__ == "__main__":
lock = threading.Lock()
finished_items=0
worker_queue=queue.Queue()
for i in range(100000):
worker_queue.put(i)
for i in range(5):
worker_thread=threading.Thread(target=worker,args=(worker_queue,lock))
worker_thread.start()
log_thread = threading.Thread(target=log, args=(worker_queue,) )
log_thread.start()
log_thread.join()
Ich habe dir schon gesagt, dass der Zugriff auf die queue NICHT mit einem Lock geschützt wird. Die queue hat alle dafür notwendigen Mechanismen selbst an Bord. Im Gegenteil, du läufst Gefahr durch das extra lock ein deadlock zu erzeugen.
Und so wie du das jetzt gebastelt hast, kommt genau das, was du in diesem Thema angesprochen hast, als Fehler vor: du denkst, += wäre atomar. Ist es nicht. Wurde auch schon erwähnt. Und genau darum bräuchte es ein lock.
Das Minimalbeispiele globale Variablen nutzen ist kein Grund, das in nicht-Trivialem Code auch zu machen. Man benutzte keine globalen Variablen. Das ist seit 50 Jahren so.