Rückgabewert vom Thread auffangen?

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
naheliegend
User
Beiträge: 439
Registriert: Mittwoch 8. August 2018, 16:42

Hey,

wenn ich einen Thread deklariert habe und in der run einen return festgelegt habe:

Code: Alles auswählen

def run(self):
	return [zahl1, zahl2]
und ich in der main den Thread aufrufe mit

Code: Alles auswählen

Auffangen = Beispiel.start()
dann möchte ich

Code: Alles auswählen

Auffangen = [zahl1, zahl2]
haben. Ist das so richtig?

Gruß
__backjack__: "Jemand der VB oder PHP kann, der also was Programmieren angeht irgendwo im negativen Bereich liegt (...)"
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

Du kannst es einfach ausprobieren. Und nein, das Hauptprogramm weiß ja nicht, wann der Thread fertig ist. Das muß man entweder low-level per Queues machen oder benutzt was aus multiprocessing.
naheliegend
User
Beiträge: 439
Registriert: Mittwoch 8. August 2018, 16:42

Sirius3 hat geschrieben: Montag 27. August 2018, 18:32 Du kannst es einfach ausprobieren. Und nein, das Hauptprogramm weiß ja nicht, wann der Thread fertig ist. Das muß man entweder low-level per Queues machen oder benutzt was aus multiprocessing.
Der Thread iteriert etwas durch, und wenn der was gefunden hat, soll er [zahl1, zahl2] ausgeben, wenn nicht, soll er 0 ausgeben...

Magst du das mit den Multiprocessing oder Low-level per Queues etwas genauer beschreiben? Bin Maschinenbauer... :mrgreen:
__backjack__: "Jemand der VB oder PHP kann, der also was Programmieren angeht irgendwo im negativen Bereich liegt (...)"
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie man multiprocessing benutzt, steht in der Python-Dokumentation.
Kannst Du näher beschreiben, was Du machen willst?
naheliegend
User
Beiträge: 439
Registriert: Mittwoch 8. August 2018, 16:42

Großen Quatsch. :lol:

Also ich habe eine Liste mit Zahlen. Und ich muss die beiden Zahlen in der Liste finden, die zusammen k ergeben.
Ich möchte das mit mehren Threads durchfeuern, damit ich nicht jede Zahl einzeln festhalten muss, um die Addition mit den restlichen zu prüfen.
Sobald ein Thread dann die Lösung gefunden hat, soll er die Indizes der beiden Zahlen in der Liste returnen und alles abbrechen.
Wenn der Thread die Lösung nicht gefunden hat, dann soll er 0 returnen.

Dafür muss ich natürlich den return der Threads abfangen. Was nicht funktioniert, bzw. was ich nicht weiß wie es geht.

Die Threads werden Iterativ erstellt und direkt kontrolliert.
__backjack__: "Jemand der VB oder PHP kann, der also was Programmieren angeht irgendwo im negativen Bereich liegt (...)"
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

also CPU-intensive Sachen wie diese auf Threads aufzuteilen macht zumindest für CPython, der Referenzimplementierung von Python (die die meisten benutzen) wenig Sinn, weil immer nur ein Thread gleichzeitig läuft (Stichwort: GIL). Da macht Multiprocessing mehr Sinn, weil Python-Prozesse parallel laufen können.
Eine alternative wäre noch z.B. die Python-Implementierung PyPy, die dürfte hier auch schneller sein als CPython.

Zum Problem: wie willst du die Liste denn sinnvolle Teilen? Sobald du die Liste teilst, könnte es doch sein, dass du die Lösung nie findest, weil es nur eine Lösung für k gibt und die beiden Summanden dummerweise in verschiedenen Listen sind.

Um alle möglichen n-elementigen Kombinationen einer endlichen Liste zu bekommt, bietet sich `combinations` aus dem `itertools` Modul an. Da kannst du dann drüber iterieren.

Gruß, noisefloor
narpfel
User
Beiträge: 690
Registriert: Freitag 20. Oktober 2017, 16:10

@naheliegend: Mit dem passenden Algorithmus ist das so schnell, dass man da mit Parallelisierung nicht signifikant gewinnt.

Mit einer Liste aus zehn Millionen Zahlen dauert es bei mir 2,8 Sekunden, alle passenden Paare zu finden.

Hinweis: Es reicht, die Liste einmal durchzugehen, denn wenn ein Summand und `k` bekannt ist, kann der andere logischerweise berechnet werden. Und schau’ dir mal den Datentypen `set` an.
Sirius3
User
Beiträge: 18267
Registriert: Sonntag 21. Oktober 2012, 17:20

Dann ist multiprocessing.Pool das richtige für Dich, weil trotz mehrerer Threads immer nur einer gleichzeitig laufen kann. Mit numpy sind so Häppchen von 10000 Zahlen wohl das richtige, bei numba eher so 100000. Sonst ist der Overhead für's kopieren zu groß.
naheliegend
User
Beiträge: 439
Registriert: Mittwoch 8. August 2018, 16:42

Habe das jetzt ohne multithreading gelöst.
__backjack__: "Jemand der VB oder PHP kann, der also was Programmieren angeht irgendwo im negativen Bereich liegt (...)"
narpfel
User
Beiträge: 690
Registriert: Freitag 20. Oktober 2017, 16:10

Meine Lösung:

Code: Alles auswählen

from random import sample, seed
from time import perf_counter


def main():
    seed(4227)
    k = 123456
    numbers = set(sample(range(10 ** 9), 10 ** 7)) | {k - 1234, 1234}

    start_time = perf_counter()
    result = set()
    for n in numbers:
        if k - n in numbers:
            result.add(tuple(sorted((n, k - n))))
    print(perf_counter() - start_time)
    
    print(sorted(result))
    

if __name__ == "__main__":
    main()

Code: Alles auswählen

» python sumofpair.py
1.9390597109959344
[(1234, 122222), (5417, 118039), (12295, 111161), (28475, 94981), (34235, 89221), (37453, 86003), (59618, 63838), (60477, 62979)]
python sumofpair.py  16,76s user 0,97s system 99% cpu 17,905 total
» pypy3 sumofpair.py
1.6923548849954386
[(1234, 122222), (5417, 118039), (12295, 111161), (28475, 94981), (34235, 89221), (37453, 86003), (59618, 63838), (60477, 62979)]
pypy3 sumofpair.py  11,96s user 1,49s system 97% cpu 13,744 total
Die Geschwindigkeitssteigerung gegenüber den 2,8 Sekunden kommt daher, dass die erste Version (im Jupyter Notebook) globale Variablen benutzt hat. Interessanterweise ist PyPy nicht wesentlich schneller.
Benutzeravatar
noisefloor
User
Beiträge: 4187
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

@naheliengend: Schön, dass du eine Lösung hat. Noch schöner wäre, wenn du die auch posten würdest. Dann hätten auch die einen Nutzen davon, die in Zukunft eiin vielleicht ähnliches Problem haben.

@narpfel: PyPy ist ~5 Sekunden schneller, dass sind bei dir ca. 30%. Das ist IMHO schon einen deutliche Verbesserung - fällt halt nur bei einer Laufzeit von 15 Sekunden nicht so ins Gewicht.

Gruß, noisefloor
naheliegend
User
Beiträge: 439
Registriert: Mittwoch 8. August 2018, 16:42

Meine Lösung für das ursprüngliche Problem ohne Multithreading:

Code: Alles auswählen

import json
import requests
import time
 
 
 
def loesung(url_chall_7, url_sol_7) -> object:
 
    def search(listoo, k):
        for index1 in listoo:
            set = index1
            for index2 in listoo:
                if index2 == set:
                  pass
                else:
                    if set + index2 == k:
                        v1 = listoo.index(set)
                        v2 = listoo.index(index2)
                        return [v1, v2] #match gefunden
                    else:
                         pass
 
 
    roh = requests.get(url=url_chall_7)
    json_roh = roh.json()
    k = int(json_roh['k'])
    lists: object = json_roh['list']
 
    solution = search(lists,k)
 
    dat = {"token": solution}
 
    result = requests.post(url=url_sol_7, data=json.dumps(dat))
    print(result.text)  # success.ausgeben falls richtig
 
 
#Main
start = time.time()
m = 100
for i in range(m):
    loesung("https://cc.the-morpheus.de/challenges/7/", "https://cc.the-morpheus.de/solutions/7/")
print((time.time() - start) / m)
__backjack__: "Jemand der VB oder PHP kann, der also was Programmieren angeht irgendwo im negativen Bereich liegt (...)"
Benutzeravatar
__blackjack__
User
Beiträge: 14033
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@naheliegend: Ein paar Anmerkungen zum Code: Typannotation an sich finde ich ja schon doof, aber Rückgabewerte und Variablen als `object` zu annotieren ist das nutzloseste was man sich vorstellen kann. Alles was man in Python an einen Namen binden kann ist ein Objekt. Etwas als `object` zu annotieren bringt als überhaupt keinen Nutzen.

Anstelle des Kommentars ``# Main`` würde man den Code eher in eine `main()`-Funktion stecken und die nur ausführen wenn das Modul als Programm gestartet wurde, aber nicht wenn es importiert wird.

Einige Namen sind nicht gut. Abkürzungen sollte man vermeiden, damit der Benutzer nicht raten muss was der Name bedeutet. Statt `url_sol_7` zum Beispiel besser `solution_url`.

Funktions- und Methodennamen beschreiben in der Regel Tätigkeiten. `loesung` wäre ein guter Name für eine Lösung, aber nicht für eine Funktion die die Lösung berechnet.

Wenn man Funktionen verschachtelt, lassen sich die inneren Funktionen nicht separat testen. Das `search()` *in* `loesung()` definiert wird, macht keinen Sinn.

Warum das Ergebnis von `requests.get()` an den Namen `roh` gebunden wird, verstehe ich nicht. Da ist nichts ”rohes” dran und der griechische Buchstabe wird ja auch nicht gemeint sein‽

Der Wert von `k` im JSON ist bereits eine (ganze) Zahl, da braucht man kein `int()` für. Und eine Liste mit Zahlen `lists` zu nennen ist verwirrend, denn es ist ja *eine* Liste.

Bei `listoo` frage ich mich wieder was mir dieser Name sagen will.

`index1` und `index2` sind ziemlich wahrscheinlich auch falsche Namen, es sei denn die Zahlen *in* der Liste stellen tatsächlich Indexwerte dar. Bei `v1` und `v2` ist es dann umgekehrt — das `v` lässt `value` vermuten, hier sind es dann aber tatsächlich Indizes.

Das `index1` an den Namen `set` gebunden wird macht keinen Sinn. Mal davon abgesehen das `set` schon der Name des eingebauten Mengentyps ist, und deswegen nicht überschrieben werden sollte, macht weder der Name, noch diese Umbennung an sich Sinn.

``if``- oder ``else``-Zweige mit ``pass`` sind sinnlos. Man kann die beiden ``if``\s dann auch zu einem zusammenfassen.

Wenn keine Lösung gefunden wird, dann gibt die `search()`-Funktion *implizit* `None` zurück. Das sollte man *explizit* machen, damit ein Leser nicht denkt das der Fall vergessen wurde oder das Programm an der Stelle noch nicht vollständig ist.

Zwischenstand:

Code: Alles auswählen

import json
import time

import requests
 
 
def search(values, k):
    for value_a in values:
        for value_b in values:
            if value_b != value_a and value_a + value_b == k:
                return [values.index(value_a), values.index(value_b)]
    return None


def loese(challenge_url, solution_url):
    challenge = requests.get(url=challenge_url).json()
    solution = {'token': search(challenge['list'], challenge['k'])}
    response = requests.post(url=solution_url, data=json.dumps(solution))
    print(response.text)
 
 
def main():
    start = time.time()
    iteration_count = 100
    for _ in range(iteration_count):
        loese(
            'https://cc.the-morpheus.de/challenges/7/',
            'https://cc.the-morpheus.de/solutions/7/'
        )
    print((time.time() - start) / iteration_count)


if __name__ == '__main__':
    main()
Der Code ist nicht besonders effizient wenn keine Lösung gefunden werden kann, weil die Addition kommutativ ist, und deshalb zu viel durchgetestet wird.

Wenn man das Nebenläufig löst, kann es passieren das eine andere Lösung herauskommt als bei einem rein linearen Ablauf. Da ist die Frage, ob das okay ist.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Antworten