Durchlaufzeit von Funktionen rausfinden / Datum angeben

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.
BlackJack

@Serperns66: Wenn etwas nach fünfmal herumprobieren funktioniert, heisst das nicht das Du es verstanden hast. Verstanden hat man erst etwas wenn man ohne herumprobieren und ohne einfaches übernehmen von einem Stück Quelltext ein ähnliches Problem lösen kann, in dem man den Code dafür von Grund aus selber schreibt und bei jedem Teilausdruck weiss was der bewirkt, welchen Wert und welchen Typ der hat und bei Anweisungen welchen Effekt die auf den Zustand und Ablauf des Programms haben. Den Eindruck vermittelst Du aber nicht. Von Dir kommen stattdessen Verständnisfragen, zum Beispiel zu Threads vs Futures, die nicht kommen würden wenn es verstanden worden wäre, und Fragen nach tabellarischen Gegenüberstellungen von Techniken, die man sich selber Aufstellen könnte, wenn man sich damit beschäftigt und sie versteht, und nicht zuletzt auch immer Fragen nach Beispielen, die Du dann hier und da veränderst, aber wie man an Folgefragen merkt, nicht wirklich durchdrungen hast. Das läuft auf Dauer darauf hinaus das Du immer weiter nach Beispiellösungen zu Deinen Problemen fragst und die dann irgendwie in Dein Programm bastelst.

Es spricht nichts gegen Ausprobieren und dabei lernen, nur sehe ich den letzten Teil nicht so wirklich. Ich bin nicht gegen Deine Art zu lernen weil das IMHO kein lernen ist, jedenfalls nicht selbstständig programmieren lernen.

Zum Beispiel hast Du jetzt wie es aussieht eine völlig falsche Vorstellung was Klassen sind, wofür die gut sind, und das basierend auf falschen Grundannahmen über die simpelsten Funktionsaufrufe auf Modulebene. Beides Sachen die man nach dem durcharbeiten eines guten Grundlagentutorials wohl nicht passiert wären. Dein Beispiel funktioniert nämlich problemlos wenn man bei der Definition von `rechne()` den fehlenden Doppelpunkt ergänzt. Warum sollte das auch nicht funktionieren‽ Wenn etwas nicht funktioniert muss man erklären können warum das so ist.

Ich kenne die Klasse `Boerse2Rock` nicht aber ich sehe Code wie Du die Methoden aufrufst und das sieht verdächtig falsch aus. Passt auch nicht zu Deiner Beschreibung denn der `__init__()` wird nichts übergeben, also was sollte dann in der `__init__()` an das Objekt gebunden werden? Und Du rufst auch nur eine ”Methode” auf, also wird das was in der `__init__()` definiert würde, ja auch gar nicht über mehrere Methodenaufrufe hinweg und/oder von verschiedenen Methoden verwendet. Eine Klasse fasst zusammengehörende Daten und Funktionen zu einer Einheit zusammen, oder im Extremfall auch mal nur Daten, aber eine Klasse ohne Daten macht keinen Sinn. Das wäre dann nämlich genau das selbe wie ein Modul mit Funktionen, nur eben mit zusätzlicher aber sinnfreier Syntax.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Sirius3 hat geschrieben:@Serpens66: wenn Du schon Beispiele postest, wo etwas angeblich nicht tut, dann solltest Du die schon selbst mal ausprobiert haben.
du hast recht.. der Code funktioniert, abgesehen von dem fehleden doppelpunkt.. hmm.. es gab eine Konstruktion wo es so war, wie beschrieben, ich weiß aber grad nicht mehr wie das genau aussah, möglicherweise waren da noch andere anfängerfehler drin.
Sirius3 hat geschrieben: Wie die Funktionsdefinition von submit ist, habe ich Dir gezeigt, und auch, wie man die übergeben Funktion dann auch aufrufen kann. Die "*" und "**" Syntax kann man übrigens im Tutorial nachlesen, wenn die einem unbekannt sein sollte. Die submit-Funktion von concurrent hast Du ja schon selbst benutzt, da gehe ich davon aus, dass Du den Aufruf auch verstanden hast.
Wie bereits geschrieben habe ich args und auch die * schreibweise bereits verstanden. Es gibt nur noch 2 Probleme, die ich eig oben recht deutlich gepostet habe, eines davon sogar in fettgedruckt. Das andere ist, dass ich die "eval()" funktion verwende, was ich ja nicht tun sollte. Leider fällt mir keine Alternative ein. Anhand der submit Funktion kann ich nicht erkennen, welchen Typ der Paramater hat, das als fn in submit reingegeben wird. Ich verwende ja einen String. Ich gehe davon aus, wenn ich keinen String verwende, dass die Funktion sonst direkt ausgeführt wird. Beim schreiben dieses Textes wäre meine Vermutung jetzt, dass ich als fn nicht z.b. get_balance(*args) übergeben muss, sondern lediglich get_balance. Ist das korrekt? Das ist alles was ich wissen woltle, wie ich die Funktion übergeben kann, ohne dass sie ausgeführt wird. Im nachhinein ist es wohl das, was du mir mit deinem hinweis hier http://www.python-forum.de/viewtopic.ph ... 35#p269935 sagen wolltest. Ich habe es aber nicht gesehen, dass "fn" hier lediglich der Funktionsname ist, ohne klammer. Hätte man drauf kommen können, bin ich aber nicht... das war das missverständnis.
(bitte lest meine Posts niemals "aggressiv" innerlich vor, denn ich formuliere meine Texte innerlich immer sehr ruhig und argumentativ ;) )

So, falls ich mit obigen Folgerungen richtig liege, sollte das eval Problem sogar gelöst sein, bleibt nur noch der Aufruf der args Variablen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Serpens66 hat geschrieben:Leider fällt mir keine Alternative ein. Anhand der submit Funktion kann ich nicht erkennen, welchen Typ der Paramater hat, das als fn in submit reingegeben wird. Ich verwende ja einen String. Ich gehe davon aus, wenn ich keinen String verwende, dass die Funktion sonst direkt ausgeführt wird.
Funktionen sind ganz normal Objekte, die kannst du auch ganz normal übergeben. In der Dokumentation wird zum Beispiel die pow-Funktion übergeben. Wenn du die () am Ende einer Funktion weg lässt, dann wird sich nicht aufgerufen.
Serpens66 hat geschrieben:(bitte lest meine Posts niemals "aggressiv" innerlich vor, denn ich formuliere meine Texte innerlich immer sehr ruhig und argumentativ ;) )
Deine Beiträge kamen jetzt nicht aggressiv rüber, bei textueller Kommunikation kann man die genaue Stimmung des Autors eh nie so genau erahnen. Die Antworten von uns sind ja auch eher sachlicher Natur, dass hat etwas trockene Materie manchmal so an sich. Gerade, wenn es schnell geschrieben wird.
Das Leben ist wie ein Tennisball.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

@ BlackJack:
ich danke euch für eure Geduld mit mir und hoffe dass ihr mich nicht aufgebt..
Du hast recht, dass man zumindest die Grundlagen beherrschen sollte und das auch nicht soo lange dauern sollte, diese zu lernen, wie dann die fortgeschrittenen Dinge.
BlackJack hat geschrieben:@Serperns66: Wenn etwas nach fünfmal herumprobieren funktioniert, heisst das nicht das Du es verstanden hast. Verstanden hat man erst etwas wenn man ohne herumprobieren und ohne einfaches übernehmen von einem Stück Quelltext ein ähnliches Problem lösen kann, in dem man den Code dafür von Grund aus selber schreibt und bei jedem Teilausdruck weiss was der bewirkt, welchen Wert und welchen Typ der hat und bei Anweisungen welchen Effekt die auf den Zustand und Ablauf des Programms haben. Den Eindruck vermittelst Du aber nicht. Von Dir kommen stattdessen Verständnisfragen, zum Beispiel zu Threads vs Futures, die nicht kommen würden wenn es verstanden worden wäre, und Fragen nach tabellarischen Gegenüberstellungen von Techniken, die man sich selber Aufstellen könnte, wenn man sich damit beschäftigt und sie versteht, und nicht zuletzt auch immer Fragen nach Beispielen, die Du dann hier und da veränderst, aber wie man an Folgefragen merkt, nicht wirklich durchdrungen hast. Das läuft auf Dauer darauf hinaus das Du immer weiter nach Beispiellösungen zu Deinen Problemen fragst und die dann irgendwie in Dein Programm bastelst.
Also ich sehe es wie folgt:
Ich habe die Sprache C4Skript von dem bereits erwähnten Spiel Clonk genau aufdieselbe Weise gelernt. Dort hatte ich allerdings deutlich leichteren Zugang zu tausenden Beispielen aller Art. Es hat tatsächlich recht lange gedauert, bis ich etwas wirklich verstanden habe. Ich habe hunderte Fragen gestellt, zu Anfang Dinge zu absoluten Basics. Ich habe Kostruktionen einfach nur abgewandelt um es für meine Zwecke zu gebrauchen, ohne wirklich zu verstehen, was da nun passiert. Ich wusste auch tagelang danach immernoch nicht, wie es nun eigentlich genau funktioniert und konnte es nicht selbst schreiben. Stattdessen habe ich immer wieder beim Beispiel nachgeschaut, wie das nochmal war. Aber ich habe trotzdem immer was dabei gelernt. Denn nach ca einem halben Jahr hatte ich nicht nur unglaublich viel Content selbst zusammengebastelt, sondern ich wusste auch, wie all die Dinge nun genau funktionieren, da ich dieselben Beispiele immer und immer wieder für eine neue Anwendung abwandeln musste.
Wenn ich mir heute meine Anfängerfragen im Clonkforum durchlese, denke ich mir auch:
"oh man, ein wunder dass ich überhaupt was auf die Reihe gekriegt habe, dass diese konstruktion so funktioniert ist nur ein glücklicher zufall! :D "
Genauso wird es mit sicherheit auch bei Python sein. Es braucht seine Zeit bis ich die etwas komplexeren Dinge wirklich verstanden habe. Bis es soweit ist, schlage ich mich mit Beispielen durch und lerne dabei. Nicht von heute auf morgen, aber durch ständiges verwenden und abwandeln des Beispiels erkenne ich mit der Zeit alle möglichen Facetten.
BlackJack hat geschrieben: Zum Beispiel hast Du jetzt wie es aussieht eine völlig falsche Vorstellung was Klassen sind, wofür die gut sind, und das basierend auf falschen Grundannahmen über die simpelsten Funktionsaufrufe auf Modulebene. Beides Sachen die man nach dem durcharbeiten eines guten Grundlagentutorials wohl nicht passiert wären. Dein Beispiel funktioniert nämlich problemlos wenn man bei der Definition von `rechne()` den fehlenden Doppelpunkt ergänzt. Warum sollte das auch nicht funktionieren‽ Wenn etwas nicht funktioniert muss man erklären können warum das so ist.
Ja stimmt, da habe ich einen Error erzeugt, etwas im Code geändert und falsche Schlussfolgerungen zu der Lösung gezogen.
Das passiert unweigerlich bei meiner Art zu lernen. Das meinte ich weiter oben mit "Ein wunder dass es überhaupt funktioniert", weil es natürlich häufiger vorkommt, dass ich dadurch falsche Annahmen habe. Aber früher oder später merke ich das entweder selbst, oder ihr weißt mich darauf hin. Oder ich lese den Hinweis zufällig beim durchstöbern eines Tutorials. Es ist ja nicht so, dass ich mit jeder kleinen Frage hier ankomme, ich versuche schon so gut ich kann meine Fragen selbst zu beantworten, sonst wäre das Forum echt überflutet mit meinen Fragen :D
BlackJack hat geschrieben: Ich kenne die Klasse `Boerse2Rock` nicht aber ich sehe Code wie Du die Methoden aufrufst und das sieht verdächtig falsch aus. Passt auch nicht zu Deiner Beschreibung denn der `__init__()` wird nichts übergeben, also was sollte dann in der `__init__()` an das Objekt gebunden werden? Und Du rufst auch nur eine ”Methode” auf, also wird das was in der `__init__()` definiert würde, ja auch gar nicht über mehrere Methodenaufrufe hinweg und/oder von verschiedenen Methoden verwendet. Eine Klasse fasst zusammengehörende Daten und Funktionen zu einer Einheit zusammen, oder im Extremfall auch mal nur Daten, aber eine Klasse ohne Daten macht keinen Sinn. Das wäre dann nämlich genau das selbe wie ein Modul mit Funktionen, nur eben mit zusätzlicher aber sinnfreier Syntax.
Hm.. ich weiß gerade leider nicht, ob du recht hast und ich meinen Aufbau fälschlicherweise als gut ansehe, oder ob du dir meinen Aufbau einfach nur falsch vorstellst (was daran liegt, dass ich hier sehr selten wirklich fertigen Code schreibe, sondern immer nur stark vereinfachte test-skripte, die so natürlich nicht bei mir verwendet werden).

Daher hier mal meine Klassen init, wie sie zurzeit aussieht:

Code: Alles auswählen

class Boerse2Rock:

    def __init__(self):
        # lokale variablen definieren
        # Anpassbare Werte (gebühren iwann noch abfragen)
        self.fee_boerse2 = 0.3/100 
        self.fee_rock = 0.5/100  
        self.maxmenge = 0.1  # eine maximale Menge pro Trade, wenn keine maximalmenge, dann 0 eintragen
        self.pairCode_boerse2 = "XXBTZEUR"
        self.pairCode_rock = "BTCEUR"
        self.mindest_profit_standard = 0.5 # mindestprofits bei 50:50 Verteilung auf den Börsen, mind. 0.1 
        self.MyBalanceBTC_boerse2 = None
        self.MyBalanceEUR_boerse2 = None
        self.MyBalanceBTC_rock = None
        self.MyBalanceEUR_rock = None
        self.MyBalanceBTCinEUR_boerse2 = None
        self.MyBalanceBTCinEUR_rock = None
        self.trademenge = None
So. innerhalb der zahlreichen Funktionen dieser Klasse, verwende ich diese Variablen, bzw. weise ihnen werte zu.
Auf diese Weise muss ich diese Variablen nicht ständig von Funktion zu Funktion weiterreichen.
Das ist doch der Sinn davon, oder nicht? Was ist daran nun falsch oder müsste man noch verbessern?
Zuletzt geändert von Serpens66 am Samstag 10. Januar 2015, 22:48, insgesamt 2-mal geändert.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

EyDu hat geschrieben: Funktionen sind ganz normal Objekte, die kannst du auch ganz normal übergeben. In der Dokumentation wird zum Beispiel die pow-Funktion übergeben. Wenn du die () am Ende einer Funktion weg lässt, dann wird sich nicht aufgerufen.
danke dir :) das war das, was ich im letzten Post ja gemutmaßt habe und mir vorher nicht bewusst war, weshalb ich auf strings zurückgegriffen habe :) Hab zwar natürlich das Beispiel mit pow gesehen, aber ich bin irgendwie trotzdem nicht drauf gekommen :D sieht man den Wald vor lauter Bäumen nicht :)
EyDu hat geschrieben: Deine Beiträge kamen jetzt nicht aggressiv rüber, bei textueller Kommunikation kann man die genaue Stimmung des Autors eh nie so genau erahnen. Die Antworten von uns sind ja auch eher sachlicher Natur, dass hat etwas trockene Materie manchmal so an sich. Gerade, wenn es schnell geschrieben wird.
Freut mich, dass sie nicht so rüberkommen, sollen sie ja auch nicht :) (hatte nur die Befürchtung, dass sie evtl ziemlich unfreundlich rüberkommen könnten, wenn man sie in einem aggressiven Ton vorliest)
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Soooo, nun sieht es so aus:

Code: Alles auswählen

    def machwas(self):
        args1 = ["Boerse2","ZEUR"]
        args2 = ["TheRock","EUR"]
        args3 = ["TheRock","BTCEUR"]
        args4 = ["Boerse2","XXBTZEUR",10]
        args5 = ["TheRock","BTCEUR"]
        args6 = ["Boerse2","XXBTZEUR"]   
        # nur hier so in der antwort:
        self.parallel(
        [self.get_balance,self.get_balance,
        self.get_orderbook,self.get_orderbook,
        self.get_ticker_exchangepair,self.get_ticker_exchangepair],
        [args1,args2,args3,args4,args5,args6])
        # wäre das auch im Skript so in der Form okay, oder ist es anders besser? 
            
    def parallel(self,aufgabenliste, argsliste): # aufgabenliste enthält  funktionen, argsliste die argumente
        print("Zeit bei parallelstart: {}".format(uhrzeit()))
        threads = list(map(Multithread, aufgabenliste, argsliste)) 
        for thread in threads:
            thread.start()
        for thread in threads:
            thread.join()
        print(list(thread.result for thread in threads))    
        print("Zeit bei Ende: {}".format(uhrzeit()))
 
class Multithread(threading.Thread):
    def __init__(self, aufgabe,argumente):
        threading.Thread.__init__(self)
        self.aufgabe = aufgabe  
        self.argumente = argumente        
        self.result = None  
        
    def run(self):
        result = self.aufgabe(*self.argumente)
        self.result = result       
        
TheTest().machwas()
Es funktioniert einwandfrei und ist glaube ich auch ganz okay geschrieben, oder?

Ich hab vorher noch ein wenig mit concurrent.futures rumexperimentiert, aber es noch nicht geschafft das spam, ham, eggs Beispiel umzusetzen. Eventuell beschäftige ich mich damit später nochmal, da meine Umsetzung natürlich deutlich Fehler anfälliger und sicherlich auch weniger effizient ist, als dieses fertige Modul. Das mache ich aber vermutlich erst in 3-4 wochen, wenn ich wieder etwas mehr Zeit habe.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Zeilen 2 bis 7 sind noch sehr unschön, da könntest du gleich eine Liste produzieren, statt sie so umständlich über Namen in Zeile 13 zusammenzusetzen. Das ist ja sehr viel unnötige Schreibarbeiten. Allerdings kannst du das gleich besser lösen: Zusammengehörende Daten sollten im Code auch immer zusammenstehen. Du hast aber zwei parallele Listen: Eine mit den Funktionen und eine mit den dazugehörigen Parametern. Stelle deinen Code mal so um, dass Funktionen und Parameter in einem Tupel gehören. Also

Code: Alles auswählen

def machwas(self):
    modes = [
        (self.get_balance, ("Boerse2", "ZEUR")),
        (self.get_balance, ("TheRock", "EUR")),
        ...
Da musst du dann natürlich die parallel-Funktion noch umstellen.

Zeile 34 und 35 kannst du auch zusammenfassen, den Umweg über "result" brauchst du nicht. Das habe ich in meinem Beispiel nur eingefügt, damit die Ausgabe des Threads auch garantiert vor der Ausgabe der Summe kommt.
Das Leben ist wie ein Tennisball.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Vielen Dank EyDu :)

sieht dann jetzt so aus:

Code: Alles auswählen

    def machwas(self):
        modes = [
        (self.get_balance, ("Boerse2", "ZEUR")),    
        (self.get_balance, ("TheRock", "EUR")),
        (self.get_orderbook, ("TheRock","BTCEUR")),
        (self.get_orderbook, ("Boerse2","XXBTZEUR",10)),    
        (self.get_ticker_exchangepair, ("TheRock","BTCEUR")),
        (self.get_ticker_exchangepair, ("Boerse2","XXBTZEUR"))
        ]
        #im richtigen Skript könnte man hier anstatt "XXBTZEUR" natürlich self.pairCode_boerse2 nehmen
        self.parallel(modes)
            
    def parallel(self,aufgabenliste): 
        print("Zeit bei parallelstart: {}".format(uhrzeit()))
        threads = list(map(Multithread, aufgabenliste)) 
        for thread in threads:
            thread.start()
        for thread in threads:
            thread.join()
        print(list(thread.result for thread in threads))    
        print("Zeit bei Ende: {}".format(uhrzeit()))
 
class Multithread(threading.Thread):
    def __init__(self, aufgabe):
        threading.Thread.__init__(self)
       
        self.aufgabe = aufgabe[0]    # ist das so vernünftig gelöst, oder gibt es da noch einen sinnvollereren Weg?
        self.argumente = aufgabe[1]        
        self.result = None  
        
    def run(self):
        self.result = self.aufgabe(*self.argumente)  
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@Serpens66: Du hast nicht verstanden, was objektorientierte Programmierung ist. Du hast anscheinend zwei Börsen und willst vin ihnen balance, order und exchangepair wissen. Eine Börse ist damit ein typisches Objekt, nach außen hin die selbe Information, dahinter stecken aber unterschiedliche URLs, Abfage-APIs, etc.
Du behandelst die Börsen aber nicht als Objekte, sondern baust sie in ein Überobjekt, das in Wirklichkeit nichts anderes ist als ein Modul mit Funktionen und globalen Variablen. Man kann auch globale Variablen erzeugen und sich die selben Probleme einhandeln, auch wenn man nicht explizit "global" schreiben muß.
get_balance bekommt als erste Argument "Boerse2" oder "TheRock", ist aber eine Funktion des Überobjekts. Bei OOP wäre get_balance eine Methode des jeweiligen Börsen-Objekts und wüßte damit implizit, welche Börse abzufragen wäre.

Futures sind für den Anfänger normalerweise einfacher zu verstehen, als Threads. Man startet die Funktion und fragt irgendwann später, wenn es gebraucht wird, das Ergebnis ab. Das erfordert normalerweise nur einen minimalen Eingriff in ein Programm, das bisher linear ablief. Was Du mit Deiner Multithreadedparellelklasse machst, ist, Dein ganzen Programm auf den Kopf zu stellen, nur damit ein paar Abfragen parallel ablaufen. Dazu steckst Du Dinge in eine Liste, die nicht zusammengehören (balance und orderbook) die Du später wieder mühsam trennen mußt. Die Komplexität verzehnfacht sich gegenüber Futures und die Fehleranfälligkeit verhundertfacht sich.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Sirius3 hat geschrieben:@Serpens66: Du hast nicht verstanden, was objektorientierte Programmierung ist. Du hast anscheinend zwei Börsen und willst vin ihnen balance, order und exchangepair wissen. Eine Börse ist damit ein typisches Objekt, nach außen hin die selbe Information, dahinter stecken aber unterschiedliche URLs, Abfage-APIs, etc.
Du behandelst die Börsen aber nicht als Objekte, sondern baust sie in ein Überobjekt, das in Wirklichkeit nichts anderes ist als ein Modul mit Funktionen und globalen Variablen. Man kann auch globale Variablen erzeugen und sich die selben Probleme einhandeln, auch wenn man nicht explizit "global" schreiben muß.
get_balance bekommt als erste Argument "Boerse2" oder "TheRock", ist aber eine Funktion des Überobjekts. Bei OOP wäre get_balance eine Methode des jeweiligen Börsen-Objekts und wüßte damit implizit, welche Börse abzufragen wäre.
Ich denke mal mit objektorientierter Programmierung meinst du das, was BlackJack letztens angesprochen hatte mit "die funktion die rechnet sollte garnicht wissen, woher die Werte kommen", richtig? Falls ja, hatte ich diesbezüglich ja bereits geschrieben, dass mein Skript quasi nur aus einer Funktion bestand, wo ich alles drin gemacht habe, was ich aber ändern möchte (edit: siehe hier: http://www.python-forum.de/viewtopic.ph ... 86#p269886 ) . Es ist noch lange nicht fertig, aber ich denke ich bin auf einem guten Weg. So habe ich deswegen nämlich diese Funktionen get_balance und Co eingeführt.
Die get_balance hat den Zweck, dass ich dort durch Eingabe des Börsennamen, bei jeder beliebigen Börse (die ich evtl noch einbaue), die Balance abfragen kann, ohne dass ich auf die objektspezifischen Dinge (z.b Argumente und wie die Ausgabe aussieht) achten muss.

Code: Alles auswählen

            def get_balance(self,exchange,currency=0):
                if exchange =="TheRock":
                    MyBalance = rock.GetBalance(currency)["result"][0]['trading_balance']
                elif exchange == "Boerse2":
                    MyBalance = k.query_private("Balance")["result"] # in einer Anfrage werden alle balances abgefragt.
                    if currency:    # ohne Angabe von currency erhält man also eine Liste mit allen currencies
                        MyBalance = MyBalance[currency]
                else:
                    print("Ungültiger Exchange Name bei get_balance übergeben!")
                    return None
                return MyBalance
                # in meinem Skript ist noch ein try/except Aufbau um Fehler zu behandeln. 
                #Er ist noch nicht besonders schön und das weiß ich, deswegen lasse ich ihn hier erstmal weg, 
                #da es darum gerade nicht geht.
So. Nun habe ich ja glücklicherweise fast fertige Objekte zur datenausfrage bei den Exchanges gefunden. Auf diese Objekte, die vorher natürlich importiert werden, wird hier zugegriffen, um die Balance abzufragen. So greift rock.GetBalance(currency) auf folgende Funktion in einem anderen Skript zu:

Code: Alles auswählen

      # get the balance for a certain currency (return None if not found) 
    def GetBalance(self, currency):
        try:
            url = 'https://www.therock.com/api/get_balance'
            values = {
            'username' : self.username,
            'password' : self.password,
            'api_key'  : self.key,
            'type_of_currency' : currency
            }     
            Balance = requests.post('https://www.therock.com/api/get_balance', data=values).json()
            return Balance
        except: # dass das hier nicht gut gelöst ist, wissen wir ja, deswegen nochmal die eigene fehlerbehandlug
            return None
Für Boerse2 gibt es eine ähnliche Funktion in einem anderen Skript, welches auch importiert ist und mit k. benannt ist.
Da für diese beiden Funktionen in den externen Skripten unterschiedliche Parameter gebraucht werden und die Ausgabe auch unterschiedlich zerlegt werden muss, bietet sich eine "get_balance" Funktion denke ich sehr an, oder etwa nicht?

Dennoch ist mein Aufbau natürlich noch lange nicht perfekt. So wäre es mir nicht ohne weiteres möglich mal eben eine dritte börse hinzuzufügen, was zum großen Teil aber auch an den Rechnungen liegt, die noch nur für 2 börsen ausgelegt sind.
Das ist mir aber auch bewusst. Es wird nun erstmal so fertiggestellt, wo ich so gut ich kann die aufgaben verteile, es aber nicht perfekt ist. Sobald das dann steht und auch funktioniert, kümmere ich mich dann darum, dass alles so ineinander greift, dass es kein Problem ist weitere Börsen hinzuzufügen, was ja auch in meinem Interesse liegt, dass das möglichst unkompliziert geht.

Sirius3 hat geschrieben: Futures sind für den Anfänger normalerweise einfacher zu verstehen, als Threads. Man startet die Funktion und fragt irgendwann später, wenn es gebraucht wird, das Ergebnis ab. Das erfordert normalerweise nur einen minimalen Eingriff in ein Programm, das bisher linear ablief. Was Du mit Deiner Multithreadedparellelklasse machst, ist, Dein ganzen Programm auf den Kopf zu stellen, nur damit ein paar Abfragen parallel ablaufen. Dazu steckst Du Dinge in eine Liste, die nicht zusammengehören (balance und orderbook) die Du später wieder mühsam trennen mußt. Die Komplexität verzehnfacht sich gegenüber Futures und die Fehleranfälligkeit verhundertfacht sich.
wieso gehören balance und orderbook nicht zusammen? Sie gehören insofern zusammen, dass ich möchte, dass sie zeitgleich abgefragt werden und nicht nacheinander. Aber ja, natürlich ist das deutlich umständlicher und fehleranfälliger als mit futures, das hab ich ja in meinem letzten Post auch geschrieben. Dennoch habe ich so schonmal wieder ein wenig was gelernt, was ich allein mit der Verwendung von futures nicht gelernt hätte :) (z.b wie man funktionen und beliebige anzahlan an argumenten weitergibt).
Ich werde mich also aufjedenfall in den Semesterferien nochmal mit futures beschäftigen und es dementsprechend wieder umschreiben.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Hallo,

ich möchte diesen alten Thread nochmal ausbuddeln, weil ich eine Frage zu dem hier entwickelten Threading Code habe.

Code: Alles auswählen

def parallel(aufgabenliste):
    threads = list(map(Multithread, aufgabenliste))
    for thread in threads:
        thread.start()
    for thread in threads:
        thread.join()

class Multithread(threading.Thread):
    def __init__(self, aufgabe=0):
        if aufgabe:
            threading.Thread.__init__(self)
            self.aufgabe = aufgabe[0]  #ist ja ein Tupel aus funktion und argumenten
            self.argumente = aufgabe[1]   
            self.result = None  
        return
        
    def run(self):
        self.result = self.aufgabe(*self.argumente) 
        return
Und zwar verwende ich Threads quasi verschachtelt.
Also ich starte bei Skript-start zb. 2 Threads die beide eine funktion mit einer "while True" Schleife aufrufen.
In dieser Schleife werden dann mit weiteren Threads Funktionen aufgerufen, die API Calls machen. Jede dieser Funkionen "return"ed nach kurzer Zeit was, sodass diese Threads eigentlich alle automatisch wieder geschlossen werden müssten, sodass die gesamtanzahl an Threads wieder bei 2 liegt.
Das ganze wiederholt sich jetzt hunderte male, solange bis ich manuell das Programm abbreche.

Irgendwann bekam ich aber mal eine Fehlermeldung, dass das Programm wegen zu vielen Threads abgebrochen wurde, weshalb ich mir nun in jedem Durchlauf die aktuelle anzahl an Threads ausgeben lasse. Dabei lässt sich leider feststellen, dass ca. alle 2 Minuten ein Thread nicht geschlossen wird. Dadurch wird die Gesamtanzahl an Threads immer höher und höher.
Ich dachte zuerst, dass mein Skript vllt nur zu schnell laufen würde und habe deshalb ein paar mehr sleep befehle in die 2 Haupt-threads eingebaut, in der Hoffnung dass die ganzen kleinen threads dann geschlossen werden, aber das passiert leider nicht. Sie bleiben offenbar unendlich lange offen.

Nur warum?
Könnt ihr mir vllt ohne genaueren Code sagen, was für Gründe es geben kann, dass obwohl die Funktion, die mit dem Thread aufgerufen wurde zuende ist, der Thread dennoch nicht geschlossen wird? Und warum passiert das nur ab und zu und nicht ständig?
Meine einzige Vermutung wäre aktuell, dass das vielleicht passiert, wenn die Funktion einen Fehler raised? Ab und zu gibts nen Fehler bei nen API Call. Das könnte erklären, warum es nur langsam ansteigt.

Übrigens könnte diese steigende Anzahl von Threads auch die Erklärung für das Memory Problem sein, worüber ich mal einen Thread gemacht hatte. Wenn zig Threads nicht geschlossen werden, blockieren sie natürlich weiterhin den Arbeitsspeicher...
BlackJack

@Serpens66: Du wirst da irgendwo irgendwelche Threads nicht beenden. Du schreibst etwas von Endlosschleifen. Und falls API-Calls welche über das Netz meinen: So etwas kann ja auch mal hängenbleiben.

Das klingt insgesamt nach einer Anwendung die von Logging profitieren könnte.

Warum ist `aufgabe` mit dem Defaultwert 0 belegt wenn das doch ein Tupel sein soll? Als Wert für ”nichts” gibt es in Python den Wert `None`. Der ist als Wahrheitswert in Bedingungen auch `False`.

Wobei ein Defaultwert hier aber sowieso unsinnig ist. Du erzeugst doch keine Theads ohne Aufgabe. Die würde spätestens in `run()` dann sowieso einen `AttributeError` auslösen und zudem ist ein Thread ohne Aufgabe sinnlos.

Du rufst die `__init__` von `Thread` nicht auf. Ist ein Wunder das das so überhaupt funktioniert.

Und irgendwie klingt das als würdest Du `concurrent.futures` selbst implementieren — nur schlechter. So ein `Future` hat beispielsweise nicht nur ein Ergebnis, sondern kapselt auch Ausnahmen und stellt die zur Verfügung wenn man das Ergebnis abfragt. Und man kann die maximale Anzahl von Threads/Prozessen angeben die verwendet werden sollen.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Ich danke dir für deine Antwort :)

Ich habe hier mal ein kleines Vorwort geschrieben (da es so lang geworden ist hab ichs in Offtopic gepackt):
viewtopic.php?f=5&t=41009

Jedenfalls möchte ich dir damit mein Verhalten erklären und warum ich, obwohl es direkt in einen der ersten Posts auch von dir vorgeschlagen wird, bisher nicht auf concurrent.futures gewechselt bin.

Ich habe mir jetzt die Posts nochmal angeguckt und ich habe damals wirklich ncihts von dem verstanden, was du versucht hast mir zu sagen.
Mir ist auch nicht aufgefallen, dass ich in dem Code ProcessPoolExecutor anstatt ThreadPoolExecutor verwendet habe, obwohl du darauf hingewiesen hast, dass ich gerade Prozesse mit Threads vergleiche. Beide Begriffe waren mir damals absolut neu und ich habe erst vor kurzem gelernt, was der Unterschied ist.

Zurück zum Thema:

Jedenfalls habe ich jetzt gemerkt, wie leicht die Anwendnung von concurrent.futures ist und ich werde dies nun anstelle der Threads verwenden, danke :)

Dennoch würde ich mich freuen, wenn du mir bei der Lösung der ursprünglichen Frage helfen könntest.
Der Thread Code verwendet ja "join()". Dh. eigentlich sollte immer so lange gewartet werden, bis alle Threads ihre Aufgabe abgeschlossen haben, erst dann geht es weiter. Danach sollte der thread doch eigentlich automtatisch geschlossen werden?
In meinem Programm geht alles normal weiter, dh. alle Threadaufgaben werden abgeschlossen, entweder durch return oder durch "raise errror". Und das Programm läuft danach auch porblemlos weiter, wird also nicht aufgehalten.
Dennoch steigt die Anzahl an Threads langsam immer weiter an, weil manche, obwohl die Aufgabe beendet ist, nicht geschlossen werden.

An sich könnte ich nun einfach auf concurrent.futures wechseln und hoffen, dass sich das Problem damit erledigt hat. Aber 1. würde ich es gerne verstehen und 2. wer weiß, ob es bei concurrent.futures nicht ein ähnliches Problem gibt? Und soweit ich gesehen habe, kann ich bei concurrenct.futures nicht nachgucken, wieviele Threads noch laufen, obwohl sie beendet sein müssten.
(Falls es aber einfach an dem Thread Code liegt, also daran, dass er so schlecht geschrieben ist, und im Normalfall dieses Problem nicht existiert, dann kannst du mir das auch so direkt sagen. Ich denke wir brauchen nun keinen voll funktionierenden Thread Code entwickeln, da wir ja stattdessen concurrent haben.)

Der Code den ich nun verwenden würde wäre sowas:

Code: Alles auswählen

    pool = concurrent.futures.ThreadPoolExecutor(10)
    futures = []
    for count in range(10):
        futures.append(pool.submit(DoSomething, count,"abc"))
    concurrent.futures.wait(futures)
    for future in futures:
        print(future.result())
BlackJack

@Serpens: Was verstehst Du denn unter dem „schliessen“ von Threads? Wenn Du mit `join()` auf das Ende eines Threads wartest, dann blockiert dieser Aufruf solange bis die `run()`-Methode des Threads am Ende angekommen ist. Egal aus welchen Gründen — also sowohl wenn der Programmfluss auf ”natürliche” Weise am Ende ankommt, als auch wenn eine Ausnahme dafür sorgt.

Falls also noch Threads in Deinem Programm laufen, dann wurde entweder nirgends auf deren Ende gewartet oder es gibt (mindestens) einen Thread der auch noch läuft und auf das Ende des anderen wartet.

Das Problem mit dem Speicherverbrauch ist davon nur teilweise abhängig, denn auch ein `Thread`-Objekt dessen `run()` bereits durchgelaufen ist, verbraucht solange es irgendwie per Code erreichbar ist, noch den Speicher den es selbst und seine Attribute verbrauchen. Also beispielsweise auch das Ergebnis vom `run()` wenn das als Attribut auf dem Objekt gespeichert wurde.

Was vielleicht noch sein könnte, ist dass die fehlende Initialisierung der Basisklasse `Thread` Probleme nach sich zieht. Du machst das ja nur wenn tatsächlich eine Aufgabe übergeben wurde, das ist aber etwas was man grundsätzlich machen sollte.

Ich habe ja auch Logging vorgeschlagen. Weil man so einfach(er) den Threads auf die Spur kommen könnte die dort alle zwei Minuten einfach weiterlaufen. Das ist bei solchen länger laufenden Prozessen eigentlich Standard, sowohl um die Fehlersuche zu erleichtern, als auch generell den Programmablauf nachvollziehen zu können.

An der Stelle wo Du Dir Die Anzahl der aktiven Threads ausgeben lässt, könntest Du auch die Threads selbst auflisten und Dir eventuell vorhandene `aufgabe`- und `argumente`-Attribute ausgeben lassen, vielleicht bringt das ja bereits ein Muster zum Vorschein das Rückschlüsse auf das Problem ermöglicht.

Falls das gleiche Problem bei `concurrent.Futures` existiert, wird das irgendwann blockieren wenn alle Workerthreads beschäftigt sind und in einer Endlosschleife hängen.

Zum Codebeispiel: Du müsstest wahrscheinlich in der letzten Schleife noch mit eventuellen Ausnahmen umgehen, wie auch immer Du damit umgehen möchtest wenn so ein `DoSomething`-Aufruf kein Ergebnis sondern eine Ausnahme zur Folge hatte.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

Vielen Dank :)

Bisher läuft concurrent.futures problemlos 5 Stunden (hängt also nichts irgendwie).
Allerdings hat die Zahl der Threads die mit threading.active_count() ausgegeben werden schon leicht zugenommen...

Aktuell verwende ich noch den alten Thread Code, um die Hauptthreads zu starten. Die die dann in while Schleife laufen. Da war ich mir nicht sicher, ob ich das auch mit concurrent machen kann, da die Anzahl der Threads die diese Hauptthreads starten, stark variieren kann und ich verhindern wollte, dass es durch Zuweisung von zu wenig Workers irgendwie hängen bleibt. (zwischen 20 und 100 pro durchlauf. Nach ~20 Stunden waren es bisher 1000 active threads, weshalb alleine diese schwankung also nicht die ursache sein kann). Oder sollte ich einfach die Hauptthreads mit einer worker Anzahl von zb 10000 starten, damit es niemals hängen bleibt? (ich dann aber den fehlerfreien concurrent Code, statt dem komischen Thread Code nehme)

Daher würde ich gerne deinen Vorschlag durchführen und gucken, welche Threads das sind. Doch wie mache ich das? Ich konnte in der Doku nichts finden, wie ich mir die aufgabe der noch aktiven Threads ausgeben lassen könnte https://docs.python.org/3.4/library/threading.html

Inwiefern kann futures.result() einen Fehler raisen ? Habe gerade testweise mal einen Assertionerror in einem thread geraised. Der Thread wurde abgebrochen und das Ergebnis war nicht in futures enthalten. Dh. das einzige was so schief gehen könnte, wäre dass ich bei 20 Threads 20 Ergebnisse erwarte, aber nur 15 bekomme, bweil 5 davon einen Fehler geraised haben. Das wäre schon ärgerlich, weil ich davon ausgehe, dass die Reihenfole wie ich sei reingegeben habe, auch die Reihenfolge ist, wie ich die ergebnisse bekomme... Allerdings hatte ich soweit ich weiß noch nie ein Problem mit der Reihenfolge und bei threading selbst sollte es doch auch so funktionieren, oder nicht? Meine Funktionen raisen in der Regel auch keinen error, sie fangen Fehler selbst ab, printen den und returnen stattdessen einen standardwert (außer es ist ein Schreibfehler im Abfang-Code)
BlackJack

@Serpens66: Ich würde sagen das 1000 Threads schon zu viel sind, 10000 sind eindeutig jenseits von dem wofür man Threads gebrauchen kann. Ich weiss das ich vor Jahren mal versucht hatte so viele Threads wie möglich zu starten und war irgendwo in den Bereich von ±800 gekommen bis mir das mit Hinweis auf zu viele Threads abgebrochen wurde.

Ich wüsste auch nicht wofür man wirklich *so* viele Sachen *echt* parallel bräuchte, denn ich sehe nicht wo das Vorteile bringt. Weder bei CPU noch bei I/O. Je mehr desto besser stimmt hier ja nicht, denn die Parallelität hat mit den Resourcen auch ihre Grenzen. Man hat normalerweise keine 1000 Prozessoren/Kerne oder so viele Massenspeicher das man die I/O-Last von 1000 Threads darauf verteilen könnte ohne das I/O der Flaschenhals wird und es dann eher langsamer wird als schneller.

Bei der Doku würde ich ja fast schon wieder den alten Hasen gegenüber dem Anfänger raushängen lassen, der sich einfach nicht genug mit dem Thema beschäftigen möchte. ;-) So ziemlich gleich am Anfang der Seite, ich muss dafür noch nicht einmal runters scrollen, ist `threading.enumerate()` dessen Dokumentation mit dem Satz „Return a list of all Thread objects currently alive.“ beginnt. Wobei `Thread` ein Link auf die Doku von `threading.Thread` ist, also genau die Objekte die Du haben möchtest. Nicht enthalten in der Liste sind „[…] terminated threads and threads that have not yet been started.“

Also so etwas in dieser Richtung (ungetestet):

Code: Alles auswählen

    LOG.debug('Active threads with `aufgabe`: %s', len(threads))
    for thread in threading.enumerate():
        if hasattr(thread, 'aufgabe'):
            LOG.debug(
                '%r\n  %r\n  %r', thread, thread.aufgabe, thread.arguments
            )
Mit einem entsprechenden `Logger`-Objekt vom `logging`-Modul und dem Level `logging.DEBUG` kann man damit eine Ausgabe bekommen bei der auch gleich ein aktueller Zeitstempel mit ausgegeben wird. Sorry, wieder eine neue Baustelle, aber wie gesagt, Logging will man bei Prozessen die mehrere Stunden laufen, eigentlich immer haben. Kannst natürlich auch ``print`` verwenden.

`Futures.result()` löst dann eine Ausnahme aus wenn die Funktion dazu eine Ausnahme ausgelöst hat. Und zwar genau diese Ausnahme:

Code: Alles auswählen

In [40]: e = concurrent.futures.ThreadPoolExecutor(10)

In [41]: def f():
    ...:     assert False
    ...: 

In [42]: r = e.submit(f)

In [43]: r.result()
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-43-6401ee0d034b> in <module>()
----> 1 r.result()

/usr/local/lib/python2.7/dist-packages/concurrent/futures/_base.pyc in result(self, timeout)                                                                    
    398                 raise CancelledError()
    399             elif self._state == FINISHED:
--> 400                 return self.__get_result()
    401 
    402             self._condition.wait(timeout)

/usr/local/lib/python2.7/dist-packages/concurrent/futures/_base.pyc in __get_result(self)
    357     def __get_result(self):
    358         if self._exception:
--> 359             reraise(self._exception, self._traceback)
    360         else:
    361             return self._result

/usr/local/lib/python2.7/dist-packages/concurrent/futures/_compat.pyc in reraise(exc, traceback)                                                                
    105     def reraise(exc, traceback):
    106         locals_ = {'exc_type': type(exc), 'exc_value': exc, 'traceback': traceback}
--> 107         exec('raise exc_type, exc_value, traceback', {}, locals_)
    108 else:
    109     def reraise(exc, traceback):

/usr/local/lib/python2.7/dist-packages/concurrent/futures/thread.pyc in run(self)                                                                               
     59 
     60         try:
---> 61             result = self.fn(*self.args, **self.kwargs)
     62         except BaseException:
     63             e, tb = sys.exc_info()[1:]

<ipython-input-41-cfcdc8007ab4> in f()
      1 def f():
----> 2     assert False

AssertionError: 
Und da ändert auch `wait()` nichts dran:

Code: Alles auswählen

In [44]: r = e.submit(f)

In [45]: concurrent.futures.wait([r])
Out[45]: DoneAndNotDoneFutures(done=set([<Future at 0xb418acc state=finished raised AssertionError>]), not_done=set([]))

In [46]: _.done.pop().result()
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-46-d26524687e7c> in <module>()
----> 1 _.done.pop().result()

/usr/local/lib/python2.7/dist-packages/concurrent/futures/_base.pyc in result(self, timeout)                                                                    
    398                 raise CancelledError()
    399             elif self._state == FINISHED:
--> 400                 return self.__get_result()
    401 
    402             self._condition.wait(timeout)

/usr/local/lib/python2.7/dist-packages/concurrent/futures/_base.pyc in __get_result(self)
    357     def __get_result(self):
    358         if self._exception:
--> 359             reraise(self._exception, self._traceback)
    360         else:
    361             return self._result

/usr/local/lib/python2.7/dist-packages/concurrent/futures/_compat.pyc in reraise(exc, traceback)
    105     def reraise(exc, traceback):
    106         locals_ = {'exc_type': type(exc), 'exc_value': exc, 'traceback': traceback}
--> 107         exec('raise exc_type, exc_value, traceback', {}, locals_)
    108 else:
    109     def reraise(exc, traceback):

/usr/local/lib/python2.7/dist-packages/concurrent/futures/thread.pyc in run(self)
     59 
     60         try:
---> 61             result = self.fn(*self.args, **self.kwargs)
     62         except BaseException:
     63             e, tb = sys.exc_info()[1:]

<ipython-input-41-cfcdc8007ab4> in f()
      1 def f():
----> 2     assert False

AssertionError:
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Serpens66 hat geschrieben:Daher würde ich gerne deinen Vorschlag durchführen und gucken, welche Threads das sind. Doch wie mache ich das? Ich konnte in der Doku nichts finden, wie ich mir die aufgabe der noch aktiven Threads ausgeben lassen könnte https://docs.python.org/3.4/library/threading.html
Direkt als vierter Aufruf wird dort threading.enumerate() beschrieben. Entweder du hast es übersehen oder nicht verstanden. Jedenfalls tut die Funktion genau das, was du willst.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

enumerate habe ich sehr wohl gesehen, bin ja nicht blind =P

Aber wie ich davon nun auf die aufgerufene Funktion==aufgabe==target komme, steht dort nirgends. Wenn man das Thread object hat, steht dort nur, dass man thread.ident oder ähnliches aufrufen kann. thread.target hat nicht funktioniert, weshalb ich nicht weiter wusste.

Mit Logging habe ich mich deshalb nicht beschäftigt, weil das für mich damals als Anfänger viel zu viel neues war. Ich habe mir stattdessen mein eigenes logging geschrieben, von dem ich weiß wie es funktioniert und es reicht aus. Soll heißen es ist einfach nur eine Funktion, der ich ähnlich wie print() einen Text übergebe, der dann in eine txt Datei geschrieben wird. Dh. in dieser txt Datei steht dann alles was ich selbst berücksichtigt habe.
Logging würde doch vermutlich nicht anders funktionieren, oder? Also wenn ich nicht selbst den command "LOG.debug" oderso schreibe, dann wird das auch nicht geloggt. Somit sollte meine eigene Funktion doch erstmal ausreichen, oder nicht?
@Serpens66: Ich würde sagen das 1000 Threads schon zu viel sind, 10000 sind eindeutig jenseits von dem wofür man Threads gebrauchen kann. Ich weiss das ich vor Jahren mal versucht hatte so viele Threads wie möglich zu starten und war irgendwo in den Bereich von ±800 gekommen bis mir das mit Hinweis auf zu viele Threads abgebrochen wurde.
Ich wüsste auch nicht wofür man wirklich *so* viele Sachen *echt* parallel bräuchte, denn ich sehe nicht wo das Vorteile bringt. Weder bei CPU noch bei I/O. Je mehr desto besser stimmt hier ja nicht, denn die Parallelität hat mit den Resourcen auch ihre Grenzen. Man hat normalerweise keine 1000 Prozessoren/Kerne oder so viele Massenspeicher das man die I/O-Last von 1000 Threads darauf verteilen könnte ohne das I/O der Flaschenhals wird und es dann eher langsamer wird als schneller.
Ich wüsste nicht, wo ich behauptet habe, dass ich 1000 Threads oderso gar mehr brauche? :D
Brauchen tue ich immer nur ~10 bis 100 aufeinmal (um genau so viele API Calls zeitgleich zu machen und auf Antwort zu warten). Danach sollen die Threads ja eigentlich verschwinden, doch das tun sie manchmal nicht, wodurch sie sich auf über 1000 summieren und irgendwann das Programm wegen zuvielen Threads beendet wird, weshalb ich hier ja um Rat frage :D

Nun zum wichtigen Teil:
Danke für deinen Code. Meine Frage sollte dann wohl durch "hasattr()" beantwortet sein. Werde es gleich mal ausprobieren... (ok sehe gerade es ist nur ein check, ob aufgabe enthalten ist)
Du schreibst ja direkt "aufgabe". Also nehme ich an, dass man damit nach der Variablen namens "aufgabe" sucht?
Ich verwende jetzt ja stattdessen concurrent.futures. Dort habe ich keine Kontrolle darüber wie ich irgendwas benenne.
Wie heißt das nun? Habe im thread.py skript von concurrent.futures mal geschaut, dort wird "fn" und "args" verwendet, aber auch thread.fn gibts leider nicht..
(habs mal für meinen Thread Code getestet und da funktioniert es, das ist schonmal super :) Muss ich nur noch rausfinden, wie concurrent.futures die funktion nennt...)

(Die Sache mit dem future.result() muss ich mir nochmal in Ruhe angucken, sieht kompliziert aus)
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

ah, ich hatte noch nicht realisiert, dass concurrent.futures nur eine Hilfe bei der Anweundung von threading ist. Dh. wenn ich was über die Thread objekte wissen will, muss ich natürlich im threading modul nachschauen.
Es heißt also "_target" :)

ok...
das ergebnis über thread, target und args sieht in meinem kurzen Test zb so aus:
THREAD:<Thread(Thread-1, started daemon 7132)>
TARGET: <function _worker at 0x02BA8078>
ARGS: (<weakref at 0x028B0600; to 'ThreadPoolExecutor' at 0x0284F930>, <queue.Queue object at 0x0284F8B0>)

Das ist ja erstmal nicht sonderlich hilfreich... ich muss also einmal die argumente entschlüsseln, bzw muss auf den Namen der Funktion kommen.
Nach meiner google suche wird fn.__name__ und fn.func_name vorgeschlagen, doch beides funktioniert nicht (thread._target.func_name)...
mal weiter suchen..

edit:
okay, nur bei manchen Threads funktioniert __name__ nicht (weil _target None ist, weils der mainthread ist). Bei den anderen wird "_worker" ausgegeben... das hilft ja jetzt nicht so viel =/
hmm ne, jetzt komm ich nicht mehr weiter. Kenne mich noch zu wenig aus, um im concurrent.futures.thread script nun zu erkennen, wie ich von _worker auf die fn Funktion komme..
BlackJack

@Serpens66: Ich ging bei `aufgabe` noch von Deiner eigenen Thread-Klasse aus, wo Du das Attribut `aufgabe` eingeführt hattest. Wenn `_target` funtkioniert ist das okay, aber eher nur in Code um Fehler zu finden, nicht unbedingt in produktiv einegesetztem Code. Bei Python 2.7 haben `threading.Thread`-Objekte das Attribut zum Beispiel nicht und der Unterstrich signalisiert das es ein Implementierungsdetail ist.

Ein fertiges Logging-Modul ist halt der Standardweg und es kann auch deutlich mehr als `print()` oder etwas schnell selbst gebasteltes, und damit meine ich nicht das es mehr kann als man braucht — das vielleicht auch — sondern wirklich nützliche Sachen. wie das Formatieren mit Zeitstempeln und Logleveln, das filtern nach Logleveln, und zum Beispiel auch das loggen von Ausnahmen inklusive Traceback. Und wenn eine Anwendung grösser wird, kann man sich einen Logger pro Programmbereich erstellen und/oder eine Hierarchie aufbauen und Loglevel für die einzelnen Bereiche/Zweige einzeln konfigurieren. Bei einem grösseren Programm möchte man beispielsweise oft nicht die Debug-Ausgaben aus dem gesamten Programm sehen, sondern nur von den Bereichen wo man gerade einen Fehler sucht.

Auf der einen Seite kratzt das nur an den vielfältigen Möglichkeiten die das Modul noch bietet, auf der anderen Seite kann man für den Anfang auch einfach `logging.basicConfig()` ganz am Anfang des Programms aufrufen und erst einmal die Funktionen auf Modulebene (`logging.debug()` & Co) verwenden, was wesentlich weniger Aufwand ist als sich Logging mit der gleichen Funktionalität selbst zu schreiben.

Aus der Frage ob man den Pool am besten gleich mit 10.000 Workern erstellen sollte und das es so um die 1.000 im Betrieb sind, hatte ich halt geschlossen 1.000 wäre das was Du hast/willst und 9.000 sind dann als Raum für die, die nicht sterben wollen. :-D

So kompliziert finde ich `Futures.result()` eigentlich nicht‽ Das Ziel ist das man eine Funktion asynchron aufrufen kann ohne das man dabei etwas an API verliert. Eine Funktion kann nun mal ein Ergebnis liefern oder aber eine Ausnahme auslösen. Also wenn man die Funktion `do_something` hat, dann sollte gelten…

Code: Alles auswählen

    # irgenwelcher code
    result = do_something()
    # irgenwelcher code
…ist mehr oder weniger das gleiche wie…

Code: Alles auswählen

    # irgenwelcher code
    future = executor.submit(do_something)
    result = future.result()
    # irgenwelcher code

Und das schliesst natürlich auch mit ein, dass `do_something()` eine Ausnahme auslösen könnte und das ”irgendwelcher code” auch ein ``try``/``except`` sein könnte wie hier:

Code: Alles auswählen

  try:
      result = do_something()
  except SomeException:
      # handle exception
Also sollte sich das hier genau so verhalten:

Code: Alles auswählen

  try:
      future = executor.submit(do_something)
      result = future.result()
  except SomeException:
      # handle exception
Das `wait()` brauchst Du übrigens nicht, denn `result()` blockiert so lange bis das Ergebnis tatsächlich da ist. Oder eine Ausnahme. Du berücksichtigst den Rückgabewert von `wait()` ja auch gar nicht.
Antworten