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

@Serpens66: Das bei Dir 0 heraus kommt liegt in der Tat daran das Du Python 3 verwendest. Viel Spass beim herausfinden warum das so ist. Ist eine schöne Übung ob man weiss mit welchen Werten da operiert wird und welche Eigenschaften und welches Verhalten die jeweils haben. Ich musste selbst zweimal hingucken, aber dann war es eigentlich sonnenklar was da passiert und warum die Schleife mit dem `join()` darin auf nichts wartet und weshalb bei der `sum()`-Funktion 0 heraus kommt, obwohl die `result`-Attribute der `Thread`-Objekte gar nicht den Wert 0 sondern `None` haben, was sich gar nicht aufaddieren lässt. :-)

Ob direktes verwenden von `threading` oder eine höhere API wie `concurrent.futures` benutzerfreundlicher ist, hängt davon ab ob sich das konkrete Problem mit `concurrent.futures` gut lösen lässt oder nicht. Die Probleme mit Prozessen hatte der OP von dem anderen Thema mit dem `multiprocessing`-Modul. Mit `concurrent.futures` kann man die Probleme zwar auch bekommen wenn man Prozesse statt Threads zum ausführen verwendet, aber Probleme kann man grundsätzlich mit so komplexen Sachen wie nebenläufiger Programmierung haben wenn man sich nicht an die ”Regeln” hält.

Die Dokumentation vom `multiprocessing` enthält eine Warnung und den Hinweis was man machen muss damit das mit den vielen Prozessen unter Windows nicht passiert, und das ist auch nichts exotisches sondern etwas das man eigentlich sowieso *immer* machen sollte: Module so schreiben das man sie ohne Seiteneffekte importieren kann.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

kann ich denn das Beispiel von snafu in Post 7 des threads (kann man irgendwie direkt auf Posts verlinken?) so übernehmen, also für meine Zwecke umschreiben und ist dann im Ergebnis "dasselbe" wie mit threading von EyDu?
Und da treten dann keine Probleme mit den Prozessen auf, da ich kein multiprocessing verwende, richtig?


das 2 zu 3 Problem hab ich jetzt mithilfe von 2to3 gelöst. man muss list(map(...)) schreiben. Da wäre ich vermutlich nie von selbst drauf gekommen. Gut es ist logisch, dass wenn eine for schleife auf threads zugreift, es ja verschiedene Elemente in threads geben muss. Aber warum "map" in Python2 nun sowas darstellt, aber in Python3 nicht mehr...

edit: wobei ich mit 2to3 auch wieder Probleme hatte. Ich hab leider schon wieder vergessen, wie ich es in meinem anderen Thread gemacht hatte: http://www.python-forum.de/viewtopic.php?f=2&t=35300
Wenn ich nämlich "2to3 new.py" eingeben (wenn die datei new heißt), dann kommt die fehlermeldung, dass 2to3 nicht bekannt ist. Ich muss halt immer "python" davor schreiben, wenn ich was starten will. Also schreibe ich "python 2to3 new.py" , auch hier kennt er 2to3 nicht. also schreibe ich "python 2to3.py new.py" dann meckert er, dass er eines der beiden nicht finden kann (weil sie sich nicht imselben Ordner befinden und ich zum starten ja immer in dem entprechenen Ordner sein muss). Also hab ich new.py in den PYthon34/Tools/Scripts Ordner verschoben und es nochmal probiert und dann gings endlich...
Ich schreib das auch deswegen hier auf, weil ich es im anderen Thread nicht gemacht habe, falls ich das nochmal vergesse und was umwandeln muss :D
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Serpens66 hat geschrieben:kann ich denn das Beispiel von snafu in Post 7 des threads (kann man irgendwie direkt auf Posts verlinken?)
Ja, der Link versteckt sich unter der "Seite" links neben dem "Verfasst" Zeitstempel, konkret ist das hier http://www.python-forum.de/viewtopic.ph ... 00#p243000
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

@cofi: danke für den Hinweis :)


@all:
ich hab jetzt mal versucht das Beispiel von Snafu bezüglich concurrent.futures mit spam eggs usw. von EyDu zu befüllen. Es tut zumindest schonmal was es soll.. auch wenn man sicherlich noch viel optimieren müsste. uhrzeit() ist eine funktion, die die aktuelle Uhrzeit ausgibt, woran ich dann die zeit ablese, die der gesamte Durchlauf gedauert hat.

Code: Alles auswählen

def calculate(arg):
    print ("Calculating {}".format(arg))
    randomnumber = random.randint(1, 5)
    print("randomnumber für {} ist {}".format(arg, randomnumber))
    time.sleep(randomnumber)
    result = random.randint(1, 42)
    endresult = "{} = {}".format(arg, result)
    return endresult
 
def get_results():
    args = ("spam", "ham", "eggs")
    workers = 3
    with futures.ProcessPoolExecutor(max_workers=workers) as executor:
        for values in executor.map(calculate, args):
            print(values)
    #print ("sum =", sum(thread.result for thread in threads)) #hier steht noch der Threadteil, weiß grad nicht, wie ich auf die zuvor berechneten Werte zugreife?
    print(uhrzeit())
    return values # auch hier könnte man die zuvor berechneten werte ausgeben, wenn man möchte
 
if __name__=='__main__':
    print(uhrzeit())
    get_results()
Mal abgesehen von den bereits im Code angemerkten Dinge, arbeitet das so effizient? Ich habe mal die Zeiten verglichen. mit threading dauert der ganze Durchlauf genau so lange, wie die größte randomnumber, also sleep(randomnumber) ist.
Bei concurrent.futures dauert es so wie es hier steht (also mit 3 workern) 1-2 sekunden länger. Also falls man das nicht weiter optimieren kann, würde ich wohl zu threading tendieren.
Welchen Vorteil hat es eigentlich die Worker Zahl einstellen zu können? Bei Threading arbeiten vermutlich genau so viele Prozesse, wie gebraucht werden, oder? Und bei "concurrent.futures" muss man die Anzahl an Prozessen immer durch die workers angeben. Ich kann mir jetzt keinen Fall vorstellen, an dem es sinnvoll ist, weniger Worker einzustellen, als eigentlich nötig wären. Oder ist threading bei zuvielen Prozessen sonst überlastet, weshalb man lieber manuell weniger einstellt?
BlackJack

@Serpens66: Du vergleichst hier ein wenig Äpfel mit Birnen wenn Du im ersten Programm Threads verwendest und im zweiten dann Prozesse. Prozesse sind wesentlich teurer beim Starten und bei der Kommunikation der Werte zwischen den Prozessen weil die sich ja gerade keinen Adressraum teilen wie Threads.

Ebenfalls nicht wirklich sinvoll vergleichbar wird es unter Last wenn viele `Threads` gleichzeitig eine begrenzte Anzahl von Prozessoren belagern gegenüber so einem `Executor` der nur eine bestimmte Anzahl von Arbeitsthreads- oder Prozessen erstellt, idealerweise nicht so viele das es mehr Arbeiter gibt als Prozessoren, beziehungsweise Kerne, denn mehr als einer kann nicht wirklich eine CPU voll benutzen. Wenn es mehr Arbeiter als CPUs werden teilen die sich die CPU.

Last ist auch so ein Stichwort: `sleep()` ist das Gegenteil von Last. Man kann locker 50 Threads erzeugen die alle 5 Sekunden schlafen und das wird nach etwas mehr als 5 Sekunden fertig sein, weil man dafür so gut wie keine CPU braucht. Wenn Du aber 50 Threads erzeugst wo jeder einzelne alleine eine CPU 5 Sekunden lang von beschäftigen würde, dann geht die Zeit dafür eher so in Richtung 50*5/anzahl der Prozessoren(kerne) + Verwaltungsaufwand des Betriebssystems zum Umschalten zwischen den Threads. (Hier ist noch nicht das „global interpreter lock” (GIL) von CPython berücksichtigt, welches prozessorlastintensive Threads in Python sowieso extrem ausbremst.)
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

@BlackJack:
ooookay.. ich glaub die hälfte habe ich verstanden ^^

Kannst du mir denn nun für mein Vorhaben sagen, was besser geeignet wäre? Ich möchte einfach gleichzeitig mehrere API anfragen an unterschiedliche Seiten schicken und die Ergebnisse auswerten. Eine API Abfrage dauert so 1-2 sekunden, grob geschätzt, weshalb es halt sinnvoll ist, das gleichzeitig und nicht nacheinander zu machen.

Bzw. brauchst du mir eig nur sagen, ob threading geeignet ist. Das gefällt mir vom Aufbau besser und ich habe es soweit auch gut verstanden und kann es denke ich problemlos auf mein Vorhaben anwenden :)

edit:
weil ich grad dafür keinen extra thread aufmachen möchte:
wie heißt eine Funktion, die so arbeitet wie das hier: http://www.clonk.de/docs/de/sdk/script/fn/BoundBy.html ?
Habe leider keine Ahnung wie man nach sowas suchen soll, da es offensichtlich nicht "boundby" heißt. Habe schon mehrere webseiten über max() und min() abgesucht, ob da auch die gesuchte Funktion erwähnt wird, weil es ja ein ähnliches Verhalten ist...
(zur Not könnte man es auch mit einer if-Verschachltelung lösen)
BlackJack

@Serpens66: Was besser geeignet ist kann man nicht so einfach sagen. Wenn es nicht um CPU-Last geht sondern primär darum die Wartezeiten beim IO zu parallelisieren würde ich keine Prozesse starten. Ob nun etwas selbst geschriebenes mit `threading` oder `concurrent.future` würde ich daran festmachen wie viele Threads gleichzeitig gestartet werden. Wenn das potentiell unbegrenzt ist und man sich selber einen Threadpool implementieren muss würde ich `concurrent.futures` verwenden bevor ich so etwas selber schreibe. Ich würde wahrscheinlich grundsätzlich zu `concurrent.futures` tendieren, solange da nicht etwas gegen spricht.

So ein `bound_by()` kann man sich doch aus `min()` und `max()` ganz simpel basteln. Da braucht man keine ``if``\s für: ``max(lower, min(upper, x))``.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

In specifications, Murphy's Law supersedes Ohm's.
BlackJack

Beazley ist ja ziemlich Anti-Thread eingestellt. Solange es Vorteile bringt welche zu verwenden würde ich da jetzt nicht so stark darauf Abheben dass es ohne GIL mehr bringen würde. Und selbst wenn es von der Geschwindigkeit her gar nichts bringen sollte, kann es trotzdem praktisch oder sogar notwendig sein unabhängige Ablaufstränge zu programmieren, zum Beispiel bei den meisten GUI-Rahmenwerken wenn etwas im Hintergrund passieren soll.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Serpens66: Wenn es sowieso nur um die Parallelisierung der Wartezeit auf Web-Services geht, werfe ich einfach mal asynio in den Raum. Das ist genau dafür gemacht, und mit aiohttp scheinen Webabfragen auch nicht viel anders zu sein als mit requests. Der Overhead für die "Thread"-Verwaltung ist damit quasi 0 und man muß sich kaum Gedanken über gemeinsam genutzte Resourcen machen.
lackschuh
User
Beiträge: 281
Registriert: Dienstag 8. Mai 2012, 13:40

@pillmuncher

Wie schaut es mit multiprocessing aus?

- Threads: nein
- Multiprocessing: jein
- Iteration: ja

There are three types of bugs: your bugs, my bugs and threads
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Das hat Beazley in dem verlinkten Vortrag gesagt:
Python and Threads, you know. Threads. Okay. So.. so.. hot button topic, you know, it's like, every new version of Python comes out and people like "Oh, you got rid of the Global Interpreter Lock yet?" - "No." - "Ah, that sucks."

So, threads, you know, you talk to people... "What are they good for?" - "Nothing." And... here's the thing, actually threads in Python are awsome at doing nothing. Like, if you want to sleep or wait for IO or something, threads work great for that. Threads can wait as fast as anything out there.
Das letzte Mal, als ich nachgesehen habe, fiel das Abrufen und Einlesen einer HTML-Seite von einem Server noch unter die Rubrik IO. Da würden Beazley und ich wohl schon Threads verwenden. Oder man verwendet, wie Sirius3 schrieb, asyncio, sofern man Python 3.4 benutzt. Das einfachste wäre aber vielleicht concurrent.futures. Das gibt es schon seit Python 3.2. Das vorliegende Problem wird sogar im Beispiel in der Doku behandelt.
In specifications, Murphy's Law supersedes Ohm's.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

hmm. jetzt verwirrt ihr mich aber =/

"concurrent.futures" hatte ich im vorletzten Post von mir ja ausprobiert und es hat länger als mit threads gedauert. Auch gefällt mir das einstellen von workers nicht, das sieht doch ziemlich Fehler anfällig aus =/ Mit Threads ist die Umsetzung soweit ich das bisher beurteilen kann ziemlich leicht und vorallem verständlich. "concurrent.futures" hab ich noch nicht so wirklich verstanden.

inwiefern ist jetzt asyncio besser für mein Problem geeignet als threads? soweit ich das verstanden habe, muss ich es anstele von requests nehmen? Habe mich stundenlang in requests eingelesen, weshalb ich schon sehr gerne dabei bleiben würde, da es für die API abfragen auch wirklich sehr leicht anzuwenden ist (im vergleich zu diesem "urllib.request, urllib.parse, urllib.error" rumgehobse).

Also was spricht denn nun gegen threads innerhalb meiner Anwendung? Soweit meine Tests mit dem Skript von EyDu es zeigen, funktioniert es zumindest mit der sleep funktion ziemlich gut. Wird es das mit API abfragen nicht mehr tun?

Ist es also nun eine "Glaubensfrage" hier, wo der praktische Nutzen der 3 Möglichkeiten für Anfänger vernachlässigbar ist, oder wäre es schon wichtig, sich auch als Anfänger damit zu beschäftigen? (gibt ja viele solche feinen unterschiede, die man schon kennen sollte,aber einen Anfänger doch stark überfordern und es für diesen dann eig erstmal noch egal ist)
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Serpens66: Threads funktionieren wunderbar, wenn die Aufgabe IO bound ist. concurrent.futures verwendet wahlweise Prozesse oder Threads. In deinem Code hast du Prozesse verwendet. Versuch es mal mit concurrent.futures.ThreadPoolExecutor, wie in dem Beispiel, das ich in meinem letzten Post verlinkt habe, und schau, ob es damit schnell genug ist.

Grundsätzlich zum Thema Futures: die Idee dabei ist, dass man eine Aktion, die parallel ausgeführt werden soll und anschließend ein Ergebnis zurückliefert, nach folgendem Muster laufen lässt (Pseudocode):

Code: Alles auswählen

future = Future(action)       # <-- startet action in einem anderen Thread/Prozess
do some stuff
result = future.get_result()  # <-- falls action fertig ist, bekommt man das Ergebnis sofort, 
                              #     falls nicht, wird gewartet, bis action fertig ist.
Dabei kann man natürlich mehrere Futures parallel laufen lassen, zB. mit ThreadPoolExecutor.map(). Darüber kann man dann iterieren und bekommt die Ergebnisse in der Reihenfolge, wie sie fertigestellt wurden.
In specifications, Murphy's Law supersedes Ohm's.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

sooo.. ich hab jetzt mal ausführlich getestet und mich dabei von der ein oder anderen Website ausgesperrt, weil ich vmerutlich zuviele anfragen verschickt hab :D
Bei meinen hier geposteten Versuchen habe ich diese webseiten aber dann weggelassen und darauf geachtet, dass keine solche Fehlermeldung mit in den Ergebnissen ist.

Die in beiden Fällen abgefragten URLS sehen so aus:

Code: Alles auswählen

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/',
        'http://python-forum.de/',
        'http://python-kurs.eu/',
        'http://abc.de/',
        'http://abc.de/',
        'http://abc.de/',
        'http://abd.de/',
        'http://abd.de/',        
        'http://abd.de/',        
        'http://abd.de/',
        'http://abe.de/',
        'http://abe.de/',        
        'http://abe.de/',        
        'http://abe.de/',        
        'http://abf.de/', 
        'http://abf.de/',    
        'http://abf.de/',    
        'http://abf.de/',    
        'http://abf.de/',    
        'http://abh.de/',   
        'http://abh.de/',
        'http://abh.de/',
        'http://abh.de/',
        'http://abh.de/',
        'http://abh.de/',
        'http://abi.de/',
        'http://abi.de/',
        'http://abi.de/',
        'http://abi.de/',
        'http://abi.de/']
Das Threading Skript sieht nun so aus:

Code: Alles auswählen

class Foo(threading.Thread):
    def __init__(self, url):
        threading.Thread.__init__(self)
        self.url = url      
        
    def load_url(self,url, timeout):
        conn = urllib.request.urlopen(url, timeout=timeout)
        return conn.readall()
   
    def run(self):
        try:
            data = self.load_url(self.url,60)
        except Exception as exc:
            print('%r generated an exception: %s' % (self.url, exc))
        else:
            print('%r page is %d bytes' % (self.url, len(data)))
 
if __name__ == "__main__":
    print("Mit THREADING {}".format(uhrzeit()))
    threads = list(map(Foo, URLS))
    for thread in threads:
        thread.start()
   
    for thread in threads:
        thread.join()

    print(uhrzeit())
und liefert folgende Ergebnisse (die ergebnisse der Websiten lasse ich außer beim ersten durchlauf mal weg, da es dieselben sind, in {} sind Anmerkungen von mir hier im Forum):
Mit THREADING Fri Jan 9 15:52:39 2015
'http://www.foxnews.com/' generated an exception: HTTP Error 404: Not Found
'http://some-made-up-domain.com/' generated an exception: <urlopen error [Errno 11001] getaddrinfo failed>
'http://abi.de/' page is 896 bytes
'http://abd.de/' page is 6006 bytes
'http://abe.de/' page is 449 bytes
'http://abd.de/' page is 6006 bytes
'http://abe.de/' page is 449 bytes
'http://abe.de/' page is 449 bytes
'http://abe.de/' page is 449 bytes
'http://abd.de/' page is 6006 bytes
'http://abi.de/' page is 896 bytes
'http://abi.de/' page is 896 bytes
'http://abi.de/' page is 896 bytes
'http://abi.de/' page is 896 bytes
'http://abd.de/' page is 6006 bytes
'http://abc.de/' page is 4232 bytes
'http://abc.de/' page is 4232 bytes
'http://python-kurs.eu/' page is 17646 bytes
'http://www.bbc.co.uk/' page is 102029 bytes
'http://abf.de/' page is 7468 bytes
'http://abf.de/' page is 7468 bytes
'http://abf.de/' page is 7468 bytes
'http://abf.de/' page is 7468 bytes
'http://abf.de/' page is 7468 bytes
'http://python-forum.de/' page is 35140 bytes
'http://abc.de/' page is 4232 bytes
'http://www.cnn.com/' page is 189025 bytes
'http://europe.wsj.com/' page is 238476 bytes
'http://abh.de/' page is 86180 bytes
'http://abh.de/' page is 86180 bytes
'http://abh.de/' page is 86179 bytes
'http://abh.de/' page is 86180 bytes
'http://abh.de/' page is 86180 bytes
'http://abh.de/' page is 86180 bytes
Fri Jan 9 15:52:41 2015 {also 2 sekunden}
______________________________________
Mit THREADING Fri Jan 9 15:53:18 2015
{...}
Fri Jan 9 15:53:21 2015 {also 3 sekunden}
______________________________________
Mit THREADING Fri Jan 9 15:53:54 2015
{...}
Fri Jan 9 15:53:58 2015 {also 4 sekunden}
Das "concurrent.futures.ThreadPoolExecutor" Skript sieht so aus (also bis auf die anzahl der worker, so wie in der doku):

Code: Alles auswählen

# Retrieve a single page and report the url and contents
def load_url(url, timeout):
    conn = urllib.request.urlopen(url, timeout=timeout)
    return conn.readall()

# We can use a with statement to ensure threads are cleaned up promptly
print("Mit FUTURES {}".format(uhrzeit()))
with concurrent.futures.ThreadPoolExecutor(max_workers=34) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))
print(uhrzeit())
Das Ergebnis kürze ich mal ab:
Es waren bei allen drei Versuchen immer 3 sekunden.

Ich bin mir unsicher, was ich nun bei der Anzahl der worker schreiben muss.. Denn mit 17 workern hat es auch nur 3 sekunden gedauert. Mit 5 workern dann immerhin 4 sekunden und mit 1worker 12 sekunden. Ich dachte ich muss für jede Abfrage einen worker haben (bei 34 links also 34 worker? )


Tja...
threads -> 2,3 und 4 sekunden
"concurrent.futures.ThreadPoolExecutor" -> immer 3 sekunden

was ist nun besser ? :D Bei letzterem weiß ich noch nicht wie es funktioniert und wie man was schreiben muss. Ich könnte es also nicht ohne weiteres in unser spam, eggs, ham Beispiel umschreiben.. und das missfällt mir =/
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Serpens66: Wieso erwartest Du Zeitunterschiede? Beide Beispiele setzen Threads zur Parallelisierung ein, nur die Interfaces sind total verschieden. futures haben den Vorteil, dass man sie einfacher in einen linearen Programmablauf einbauen und quasi wie Funktionen verwenden kann.

Hier übrigens das Beispiel mit asyncio:

Code: Alles auswählen

import asyncio
import aiohttp

@asyncio.coroutine
def read_url(url):
    conn = yield from aiohttp.request('get', url)
    return url, (yield from conn.read())

def main():
    tasks = [read_url(url) for url in URLS]
    for task in asyncio.as_completed(tasks):
        try:
           url, page = yield from task
           print("%s page is %d bytes" % (url, len(page)))
        except Exception as ext:
           print("Exception: %s" % ext)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

@Sirius3: vielen dank für das Beispiel :)

Ich erwarte Unterschiede in der Zeit, weil hier ja behauptet wird, das eine sei besser geeignet als das andere :D Und ich möchte für mich selber sehen, was nun für meine Zwecke am besten ist. Die Zeit ist das einzige, was mir einfällt, was ich messen könnte, um die Unterschiede festzustellen.
Ich möchte die Unterschiede sehen, damit ich entscheiden kann, welches ich nun für mein Skript verwende.

Habe jetzt Thread gewählt und es für mein Zwecke verwendet. Hat ne Weile gedauert, aber jetzt scheint dabei alles zu funktionieren :)

Wenn du sagst, dass es keine zeitlichen Unterschiede gibt, was ist es dann, was mehr für das Eine und gegen das Andere spricht?

edit1: sehe gerade zu futures nennst du schon einen vorteil, danke :) Das war auch mein Gedanke, dass es leichter einzubauen ist (danke an pillmuncher für den Muster Pseudocode). Allerdings schreibt pillmuncher ja auch, dass man tatsächlich aber noch ThreadPoolExecutor.map() verwenden muss, was doch letzlich auf denselben (oder zumindest ähnlich hohen) Aufwand rauslaufen würde (nämlich auf eine funktion außerhalb des linearen Ablaufs zu verweisen), wie die Sache mit Threading, oder nicht?


Mein threading Skript sieht nun übrigens so aus (def parallel befindet sich in der "Boerse2Rock"-Klasse und kann halt jederzeit mit den gewünschten aufgaben aufgerufen werden... noch werden die ergebnisse einfach nur in eine liste geklatscht und geprintet, mal gucken wie ich das dann noch weiter mache) :

Code: Alles auswählen

    #sorgt dafür, dass übergebene Aufgaben parallel erledigt werden! (für aufgabennamen siehe multithread klasse)
    def parallel(self,aufgabe1, aufgabe2, aufgabe3=0, aufgabe4=0, aufgabe5=0, aufgabe6=0, aufgabe7=0):
        
        print("Zeit direkt bei parallel aufruf: {}".format(uhrzeit()))
        aufgabenliste = [aufgabe1, aufgabe2, aufgabe3, aufgabe4, aufgabe5, aufgabe6, aufgabe7]
        
        #jetzt noch alle nullen entfernen
        while 0 in aufgabenliste:
            aufgabenliste.remove(0)        
         
        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 wenn fertig: {}".format(uhrzeit()))
    
# Klasse zum parallelierisern
class Multithread(threading.Thread):
    def __init__(self, aufgabe):
        threading.Thread.__init__(self)
       
        self.aufgabe = aufgabe      
        self.result = None  
        
    def run(self):
        if self.aufgabe == "MyBalanceEUR_boerse2":
            result = Boerse2Rock().get_balance("boerse2","EUR")
        if self.aufgabe == "MyBalanceEUR_rock":
            result = Boerse2Rock().get_balance("TheRock","EUR")
        if self.aufgabe == "FullOrderBook_rock":
            result = Boerse2Rock().get_orderbook("TheRock","BTCEUR")
        if self.aufgabe == "FullOrderBook_boerse2":
            result = Boerse2Rock().get_orderbook("boerse2","BTCEUR",10)
        if self.aufgabe == "GetTicker_boerse2":
            result = Boerse2Rock().get_ticker_exchangepair("boerse2","BTCEUR")
        if self.aufgabe == "GetTicker_rock":
            result = Boerse2Rock().get_ticker_exchangepair("TheRock","BTCEUR")
        self.result = result       
        return 1

edit2:
ich merke gerade, dass die aufgabenart mithilfe von festgelegten Namen festzulegen vllt doch nicht so günstig ist... Hier sind das meiste ja nur Anfragen, die nur wenige Paramenter benötigen. Aber wenn ich nun z.b eine Kauforder einstellen will, muss ich viele Parameter und vorallem auch den Preis nennen. Das funktioniert mit dieser Namensvergabe dann also schonmal nicht.

D.h ich muss ich jetzt überlegen, wie ich etwas parallel laufen lasse, aber dennoch alle gewünschten Paramter übergeben kann...
Fällt euch da was ein? Ich würde jetzt vermutlich auf Klasseninterne Variablen zurückgreifen. Z.b eine self.preis Variable, die kurz vor dem Aufruf von "parallel(kaufebtc_1, kaufebtc_2)" eingestellt wird, damit die dann wissen zu welchem Preis.
Zuletzt geändert von Serpens66 am Samstag 10. Januar 2015, 18:20, insgesamt 1-mal geändert.
BlackJack

@Serperns66: Geschwindigkeitsunterschiede beim *warten* sind irgendwie nicht wirklich zu erwarten, egal welche Technik da letztendlich wartet. Der Unterschied ist bei der Benutzung, ob man sich mit `Thread`\s auf dem Level herum schlägt das einem das Betriebssystem bietet, oder ob man etwas nimmt was einem viel Arbeit für so einen Standardfall schon abnimmt. Zum Beispiel das mit dem `map()`: Das musst Du nicht benutzen, Du kannst Dir das auch selber mit Schleifen schreiben, aber warum willst Du Dir diesen Mehraufwand geben wenn es dass doch schon fertig gibt‽

Warum bekommt `parallel()` keine Liste als Argument? Dann wird die Implementierung deutlich einfacher und die Benutzung deutlich flexibler.

Das mit der `self.aufgabe` ist unnötig umständlich. Anstelle dieser ganzen Zeichenketten die da verglichen werden wäre es viel einfacher und flexibler das die Aufgabe eine Funktion oder Methode ist die dann ohne irgendetwas zu prüfen einfach ausgeführt wird. Dann musst Du noch Ausnahmen ordentlich behandeln, und hey Du hast `concurrent.futures` Ansatzweise nachgebaut statt das einfach zu verwenden.

Die `Thread.run()`-Methode sollte nichts zurück geben. Das wird nirgends benutzt und ist deshalb total sinnfrei, aber verwirrend weil der Leser sich fragt warum das dort steht.
Serpens66
User
Beiträge: 259
Registriert: Montag 15. Dezember 2014, 00:31

@BlackJack:
danke :)
BlackJack hat geschrieben:Warum bekommt `parallel()` keine Liste als Argument? Dann wird die Implementierung deutlich einfacher und die Benutzung deutlich flexibler.
auf die Idee bin ich nicht gekommen :D das macht die ganze Sache deutlich einfacher und ich bin dann auch nicht auf maximal 10 Aufgaben beschränkt (wegen dem 10 argumente limit)... und ich kann alle nötigen Argumente übergeben, stimmt :)
BlackJack hat geschrieben: Das mit der `self.aufgabe` ist unnötig umständlich. Anstelle dieser ganzen Zeichenketten die da verglichen werden wäre es viel einfacher und flexibler das die Aufgabe eine Funktion oder Methode ist die dann ohne irgendetwas zu prüfen einfach ausgeführt wird. Dann musst Du noch Ausnahmen ordentlich behandeln, und hey Du hast `concurrent.futures` Ansatzweise nachgebaut statt das einfach zu verwenden.
Ja, das hab ich eben zeitgleich zu deinem Post noch unter edit2 reineditiert, dass diese Namensvergebung mit Zeichenketten nicht das wahre ist...
Das heißt im Fall von "MyBalanceEUR_boerse2", übergebe ich als Aufgabe stattessen gleich:
"Boerse2Rock().get_balance("boerse2","EUR")" ? Da stellt sich mir dann nur die Frage, wann diese Funktion dann ausgeführt wird.. soweit ich weiß würde es so gleich beim Übergeben schon ausgeführt werden und nur der return Wert ankommen, wodurch es nicht mehr parallelgeschaltet wäre, oder? Wobei, wenn man es als String übergibt, unterbindet man das... und ausführen tue ich es dann mit "result = eval(aufgabe1)" ?
Freut mich, dass ich `concurrent.futures` nun ansatzweise nachgebaut habe :D xD Naja, so verstehe ich wenigstens, wie das alles funktioniert :)
BlackJack hat geschrieben: Die `Thread.run()`-Methode sollte nichts zurück geben. Das wird nirgends benutzt und ist deshalb total sinnfrei, aber verwirrend weil der Leser sich fragt warum das dort steht.
ja stimmt, ich war mir unsicher, ob der return Wert irgendwo verwendet wird, hatte erst "return result" da stehen und hatte zum testen, ob noch dasselbe ergebnis rauskommt, dann return 1 draus gemacht und festgestellt, dass es egal ist was ich returne, nur vergessen es dann zu löschen :)

Edit:
Wenn ich jetzt die übergebene string-Funktion mit eval() ausführe, dann habe ich ja ein Problem mit den Parametern in dieser string-Funktion (weil diese variablen, welche nun parameter sind, an der auszuführenden Stelle evlt nicht bekannt sind). Ich nehme mal an, dass für diesen Fall die Globals und Locals aus der eval() Funktion da sind? Wie wende ich das korrekt an?
edit2: oder muss ich hier exec() verwenden?
Zuletzt geändert von Serpens66 am Samstag 10. Januar 2015, 18:44, insgesamt 1-mal geändert.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Serpens66: natürlich darfst Du die Funktion für die Aufgabe nicht direkt aufrufen, aber Strings bringen Dich da auch nicht weiter. Schau Dir einfach mal an, wie es concurrent.futures' submit macht.
Antworten