ist es möglich zwieschen klassen die in eigenen dateien liegen und die in einem main file liegen eine queue zu verwenden

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
sauterle
User
Beiträge: 78
Registriert: Mittwoch 27. Juli 2022, 21:33

Hallo
Ich bin Python Neuling und versuche mich gerade an der Programmierung mit threads . Dafür würde ich gerne wissen wie man ein Queue-Objekt zwieschen mehreren threads , die in eigenen files liegen verwendet.
Die beiden Klassen könnte man auch in ein file legen , da die beiden Klassen aber inzwischen etwas gewachsen sind wäre es mir aus gründen der Übersichtlichkeit lieber die Klassen in zwei eigene files zu legen.
hier ein Beispielcode für das gemeinsame file , der funktioniert.

Code: Alles auswählen

import threading
import random
import time
from queue import Queue

class A(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        
    def run(self):
        for t in range(20):
            self.number = random.randint(1,10)
            self.wait = random.randint(0,99)/100
            queue.put(self.number)
            print(t)
            time.sleep(self.wait)
        queue.put("lll")
        
class B(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.booll= True
        
    def run(self):
        while self.booll != "lll":
            try:
                self.booll = queue.get( block = False )
            except:
                pass
            else:
                print(self.booll)
                
queue = Queue()

a = A()
a.start()

b = B()
b.start()

a.join()
b.join()

print("alle threads beendet")
ist es jetzt iergendwie möglich ein main file und zwei "klassen-files" zu haben wie in diesem beispiel ?

main.py

Code: Alles auswählen

from queue import Queue
from a import A
from b import B

queue = Queue()

a = A()
a.start()

b = B()
b.start()

a.join()
b.join()

print("alle threads beendet")
a.py

Code: Alles auswählen

import threading
import random
import time

class A(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        
    def run(self):
        for t in range(20):
            self.number = random.randint(1,10)
            self.wait = random.randint(0,99)/100
            queue.put(self.number)
            print(t)
            time.sleep(self.wait)
        queue.put("lll")
b.py
import threading

class B(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.booll= True

def run(self):
while self.booll != "lll":
try:
self.booll = queue.get( block = False )
except:
pass
else:
print(self.booll)
LG sauterle
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ja, aber nicht, indem du globale Variablen benutzt. Sondern indem du die Queue als Abhaengigkeit uebergibst.

Code: Alles auswählen

from modul_a import KlasseA
from modul_b import KlasseB
import queue

def main():
    queue = queue.Queue()
    a = KlasseA(queue)
    b = KlasseB(queue)
Ganz einfach.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1012
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Oder man macht Queue privat und nur die beiden Klassen dürfen _queue nutzen.

Code: Alles auswählen

import time

from threading import Thread
from random import uniform, randint
from queue import Queue


# für den bösen * import
__all__ = ("A", "B")

# Sentinel Objekt
STOP = object()
# Gemeinsame queue genutzt durch A und B
# Der führende Unterstrich ist eine Konvention, die sagt, dass
# dieses Objekt "privat" ist. D.h. es sollte von außerhalb nicht verändert werden
# Die Klassen A und B greifen auf dieses Objekt zu.
_queue = Queue()


class A(Thread):
    def __init__(self):
        super().__init__()
        # _queue stammt vom modul
        self.queue = _queue

    def run(self):
        for _ in range(20):
            number = randint(1,10)
            delay = uniform(0, 0.99)
            self.queue.put(number)
            time.sleep(delay)
        self.queue.put(STOP)


class B(Thread):
    def __init__(self):
        super().__init__()
        self.queue = _queue

    def run(self):
        element = None
        while element is not STOP:
            element = self.queue.get()
            print(element)
            self.queue.task_done()
        print("B Ende")

Nachteil: Weniger Kontrolle. Was ist, wenn die Queue ein Limit haben soll? Dann muss man wieder den Quellcode des Moduls ändern.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist immer noch eine globale Variable, und darum Mist. Und da er ja zwischen Modulen teilen will, hilft es doch auch nicht, die privat zu machen.
Benutzeravatar
__blackjack__
User
Beiträge: 13007
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Zumal ”zwischen Klassen teilen” ja gar nicht stimmt. Das wird zwischen *allen* *Exemplaren* geteilt die da erstellt werden. Kann also ”interessant” werden wenn es mehrere Sender oder Empfänger gibt. Das kann funktionieren, muss es aber nicht. Falls es das nicht tut, sollte man das entweder sehr deutlich dokumentieren oder die entsprechenden Datentypen zu Singletons machen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
sauterle
User
Beiträge: 78
Registriert: Mittwoch 27. Juli 2022, 21:33

danke ich werde das mit der abhängig nehmen
LG sauterle
sauterle
User
Beiträge: 78
Registriert: Mittwoch 27. Juli 2022, 21:33

Hallo jetzt funktioniert alles soweit, sind in diesem Code noch irgendwelche Fehler
danke für eure antworten im voraus :D

main.py:

Code: Alles auswählen

from queue import Queue

from a import A
from b import B


queue = Queue()

a = A(queue)
a.start()

b = B(queue)
b.start()

a.join()
b.join()


print("alle threads beendet")
a.py

Code: Alles auswählen

import threading
import random
import time

class A(threading.Thread):
    def __init__(self,queue):
        super().__init__()
        self.queue=queue
        self.queue.put("o")
        
    def run(self):
        for t in range(20):
            self.number = random.randint(1,10)
            self.wait = random.randint(0,99)/100
            self.queue.put(self.number)
            time.sleep(self.wait)
        self.queue.put("lll")
        
b.py

Code: Alles auswählen

import threading

class B(threading.Thread):
    def __init__(self,queue):
        super().__init__()
        self.queue=queue
        self.booll= True
        
    def run(self):
        while self.booll != "lll":
            try:
                self.booll = self.queue.get( block = False )
            except:
                pass
            else:
                print(self.booll)
LG sauterle
Benutzeravatar
__blackjack__
User
Beiträge: 13007
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sauterle: Der Code in der ``main.py`` ist nicht in einer Funktion.

Zumindest für das Beispiel macht es nicht wirklich Sinn das bisschen Code auf drei Module zu verteilen.

Beide Klassen haben neben der `queue` noch sinnlose andere Attribute die eigentlich lokale Namen sein sollten. Und bei `A` werden die auch noch ausserhalb der `__init__()` angelegt. Man muss auch nicht jedes kleine Zwischenergebnis an einen Namen binden.

`wait` ist ein komischer Name für eine Dauer. `t` wird nicht benutzt – dafür benutzt man per Kovention den Namen `_`.

"o" und "lll" sind komische Werte. Für "lll" würde man eine Konstante definieren, statt den Wert zu wiederholen.

`booll` ist ein ganz schlechter Name, selbst für einen Wahrheitswert, aber erst recht für Zahlen und Zeichenketten.

``block=False`` ist falsch. Was ist es nicht warm genug? Muss die CPU auch noch sinnlos mit heizen? 😈

``except: pass`` ist sowas von falsch.

Aus der ``while``-Schleife kann man mit `iter()` eine ``for``-Schleife machen.

Die beiden Klassen sind letztlich nicht nötig, weil man mit `Thread`-Objekten auch Funktionen asynchron ausführen kann.

Ausserdem sind zwei zusätzliche Threads unnötig wenn der Hauptthread dann nur darauf wartet das beide fertig sind, statt eine Aufgabe selber zu übernehmen.

Bleibt letztlich so etwas übrig:

Code: Alles auswählen

#!/usr/bin/env python3
import random
from threading import Thread
import time
from queue import Queue

DONE = "lll"


def produce(queue):
    queue.put("o")
    for _ in range(20):
        queue.put(random.randint(1, 10))
        time.sleep(random.randint(0, 99) / 100)
    queue.put(DONE)


def consume(queue):
    for value in iter(queue.get, DONE):
        print(value)


def main():
    queue = Queue()
    Thread(target=produce, args=[queue], daemon=True).start()
    consume(queue)
    print("Alle threads beendet.")


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Was ist ein `boll`und was bedeutet der Wert "lll"?
Wenn man innerhalb einer Schleife Attribute setzt, ist das immer ein Zeichen dafür, dass man etwas falsch macht, denn diese Attribute werden ja bei jedem Schleifendurchgang durch neue Werte überschrieben, und Attribute sollten ja nur Werte über das Ende der Methode hinaus speichern.
Für Threads braucht man eigentlich selten eigene Klassen, sondern es reicht, eine Funktion zu schreiben. Dadurch vereinfacht sich der Code drastisch.

Code: Alles auswählen

import threading
import random
import time
from queue import Queue

SOME_MAGIC_VALUE_TO_INDICATE_THE_ENDING = "lll"

def run_a(queue):
    queue.put("o")
    for t in range(20):
        number = random.randint(1, 10)
        wait = random.randint(0, 99) / 100
        queue.put(number)
        time.sleep(wait)
    queue.put(SOME_MAGIC_VALUE_TO_INDICATE_THE_ENDING)

def run_b(queue):
    while True:
        value = queue.get()
        if value == SOME_MAGIC_VALUE_TO_INDICATE_THE_ENDING:
            break
        print(value)

def main():
    queue = Queue()
    thread_a = threading.Thread(target=run_a, args=(queue,))
    thread_a.start()
    thread_b = threading.Thread(target=run_b, args=(queue,))
    thread_b.start()

    thread_a.join()
    thread_b.join()
    print("alle threads beendet")

if __name__ == "__main__":
    main()
sauterle
User
Beiträge: 78
Registriert: Mittwoch 27. Juli 2022, 21:33

danke für das feedback
zuerst natürlich ist das nur ein beispiel der eigentliche code ist wesentlich länger. das

Code: Alles auswählen

try:
                self.booll = self.queue.get( block = False )
            except:
                pass
            else:
                print(self.booll)
habe ich gewält weil man so mehrere queues gleichzeitig kontrolieren kann , danach habe ich vergessen es wieder zu entfehrnen. wo findet man heraus was konvetion ist?
LG sauterle
Benutzeravatar
__blackjack__
User
Beiträge: 13007
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sauterle: Das nackte ``except:`` ist in jedem Fall falsch, denn man weiss hier ja welche Ausnahme man erwartet und welche man sicher ignorieren kann. Aber auch der Ansatz ist falsch oder zumindest *sehr* ungünstig da 100% CPU Zeit mit nichts tun zu verbraten wenn keine Daten anliegen. Müssen es denn mehrere Queues sein? Ich würde so etwas umstrukturieren zu einer Queue in die gegebenenfalls zu jedem Datum noch eine Absenderinformation gesteckt wird, damit man das beim Empfänger dann zuordnen kann.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
sauterle
User
Beiträge: 78
Registriert: Mittwoch 27. Juli 2022, 21:33

danke für den Tipp mit der absenderinformation dieser macht alles erhelbich einfacher.
LG sauterle
Antworten