Wieso lassen so viele Programme den Benutzer so lange warten?



  • Eisflamme schrieb:

    Sorry, hab den OP geändert. Chrome friert bei mir in letzter Zeit tatsächlich ein, aber darauf wollte ich nicht hinaus.

    Wie lange dauert Threaderzeugung denn, dass das ins Gewicht fällt? Mehr als eine Sekunde? Falls nicht, würde sich das nämlich für die Fälle, die ich meine, schon lohnen. Es geht auch nicht um Berechnungen, die eine halbe Sekunde dauern, sondern über 10 Sekunden o.ä. Datenbankabfrage sind ja schon Mal so eine Sache. Wenn man in einem Programm arbeitet und eine Abfrage macht, die länger dauert als der Benutzer erwartet, wäre es doch nett, wenn er das abbrechen könnte oder an eine andere Stelle im Programm geht, je nachdem, ob man da noch was anderes nutzen kann.

    Ich habe ja mittlerweile Mal etwas in Multithreading reingeschnuppert und weiß, dass es ein gewisser Aufwand ist, auch weil die Entwicklung damit ein Gebiet ist, die sich von Programmierung mit einem Thread massiv unterscheidet. Nur na ja... ich denke, wenn es am Aufwand oder an den Programmiern liegt, dann ist das etwas, was man auf Dauer angehen sollte... sei es durch mehr Bibliotheken oder Tutorials oder eben so eine Einbindung in WinRT... aber das sind doch keine dauerhaften Probleme..?

    Es ist nicht nur "etwas Aufwand" sondern sehr viel. Außerdem ist es hoch riskant.Damit kann man sich Fehler einfangen die nicht reproduzierbar sind, sondern nur zufällig entstehen. Das menschliche Vorstellungsvermögen (Gehirn) ist nicht dafür ausgelegt alle Eventualitäten zu erkennen die bei der verschachtelten Ausführung auftreten können.
    Für den geringen Nutzen, geht kaum Jemand diesen Aufwand und vor allem nicht das Risiko ein.



  • Aber aber... ok.



  • Was dauert eig. an der Threraderzeugung so lange?
    Eigentlich muss doch nur ein bischen Speicher für Stack und Register alloziert werden sowie dem Scheduler bekannt gemacht werden. Aber sonst?

    Höchstens noch threadlocal data, aber das ist doch meistens auch gratis, wenn man es nicht nutzt.



  • "Thread start: few hundred microseconds"

    Das ist nicht "lange" aus Sicht des menschlichen Benutzers. Es ist ein nicht auszurottendes Gerücht, dass Treaderzeugung wahnsinnig teuer ist. Aus Ghz-Sicht im Nanosekundenbereich ist es nicht ganz billig.

    Ansonsten: Threadpools und nochmal Threadpools.



  • Ja, das hat mich als Argument eben auch verwundert. Mir geht es nicht darum Performance zu verbessern (wobei ich glaube, dass die meisten Algorithmen, die man parallelisieren möchte, oft auch nicht durch das Erstellen des Threads so stark ausgebremst werden), sondern eben ausschließlich um Benutzerfreundlichkeit.

    Und es geht eben um Dinge, die halt etwas länger als ein paar Sekunden dauern würden.

    Also ein Beispiel, was mir wieder vor kurzem begegnet ist: Ich habe ein Programm mit mehreren Tabs. Das Programm ist hochgradig datenbanklastig; beim Klicken auf ein bestimmtes Tab wird automatisch eine aufwendige Datenbankabfrage gestartet. Man kann allerdings auch filtern! Wenn ich vorher weiß, dass ich filtern will, die Standardabfrage allerdings 10 Sekunden dauert, stört mich das schon Mal erheblich.

    Die UI-Datentabelle zu locken und das Laden in einen anderen Thread auszulagern und eben eine Abort-Injection anzubieten, klingt für mich nicht nach besonders viel Aufwand (etwas Ähnliches habe ich vor kurzem implementiert)... Na ja, die einfachere Lösung wäre in diesem Fall vermutlich einfach gar nichts zu laden, bevor ein Filter eingegeben wird. Aber manchmal will Benutzer eben eine Standardabfrage aber ein aufwendiges Laden unterbrechen können. Und auch wenn das nicht sein muss, finde ich, dass man als Benutzer ein besseres Gefühl vom ganzen "Anwendungsfluss" hat, also dass es irgendwie zügiger geht, schnell reagiert und brav rasch reagiert.

    Vielleicht bin ich als ungeduldiger Benutzer auch einfach die Minderheit. 🙄



  • Eisflamme schrieb:

    Vielleicht bin ich als ungeduldiger Benutzer auch einfach die Minderheit. 🙄

    Ne, aber Programmierer die beruflich arbeiten, haben keine Zeit für das was nicht sein muss. Außerdem will man ja noch eine 2. Version verkaufen. Und schnellere PCs will man auch immer verkaufen.



  • Hm, aber über so was würde ich nicht versionieren, weil das nur ein Qualitätsmerkmal zusätzlich ist. Das würde ich gerne sofort reinstecken. Und schnellere PCs bringen bei aufwendigen Abfragen ja so oder so auch immer Vorteile.

    Dass man dafür einfach keine Zeit hat, okay, das verstehe ich. Kann man wohl nur angehen, wenn man sehr fit mit Threads ist oder besonders viel Zeit übrig hat.



  • Ein Problem ist auch, dass viele UI Toolkits, egal in welcher Sprache, kaum vernünftige Unterstützung für asynchrone Aufgaben haben/hatten.
    WinRT kommt jetzt mit einer guten async API, aber bei QT ist mir z.B. nichts vergleichbares bekannt.
    SwingWorker in Swing ist auch erst seit Java 1.6 in der Standardbibliothek, ähnliche Konzepte in SWT sind afaik an Eclipse gekoppelt.
    Ansonsten muss man das halt alles per Hand machen, was für viele Programmierer dann zu viel Arbeit oder zu hoch ist. 😮



  • QT bietet Concurrent::run an: http://doc.qt.nokia.com/4.7-snapshot/qtconcurrentrun.html#run

    Es wird anscheinend automatisch auf einen Threadpool zurückgegriffen. Das ist für einmalige Funktionen eigentlich ganz chic.



  • Eisflamme schrieb:

    QT bietet Concurrent::run an: http://doc.qt.nokia.com/4.7-snapshot/qtconcurrentrun.html#run

    Es wird anscheinend automatisch auf einen Threadpool zurückgegriffen. Das ist für einmalige Funktionen eigentlich ganz chic.

    Es geht nicht darum einfach nur Funktionen in andere Threads auszulagern sondern um die Kommunikation und Datenübergabe zwischen Worker-Thread und UI-Thread.



  • Und wo liegt das Problem das Ergebnis, sobald es da ist, eben ins UI zu schreiben und, falls man die Daten weiterverwenden können soll für weitere Abfragen, diese eben vorher zu blocken?

    Mir geht es gerade wie gesagt vor allem um irgendwelche Abfragen. Die holt man, hat sie und verwendet sie dann. Es soll zwischendurch kein Dritter diese Daten manipulieren oder lesen können.



  • Eisflamme schrieb:

    Und wo liegt das Problem das Ergebnis, sobald es da ist, eben ins UI zu schreiben und, falls man die Daten weiterverwenden können soll für weitere Abfragen, diese eben vorher zu blocken?

    Das Problem ist, dass GUI Toolkits single-threaded und i.d.R. nicht threadsicher sind, das heißt das Update der Daten muss im UI Thread stattfinden, sonst kann's Ärger geben.
    Viele Programmierer sind eben zu faul (oder können es nicht) lange Tasks vernünftig auszulagern und dann die GUI korrekt zu aktualisieren. Darum machen viele das lieber direkt im UI Thread (z.B. im Button-klick-handler) und blockieren damit den UI Thread. Ergebnis: Oberfläche hängt aka "friert ein", falls der Task unerwartet lange dauert.



  • Ok, aber das ist doch immer noch kein Problem. Bei QT kann man halt so nen QFutureWatcher nutzen, setzt dem halt das erstellte Future-Objekt. Wenn das Ding fertig ist, erhält man ein Signal, verbindet das zu nem Slot vom Haupt/UI-Thread und setzt dann die Daten. Man muss eben davon ausgehen, dass das UI nicht threadsafe ist und es entsprechend behandeln. Aber das ist doch kein Hexenwerk, wenn man sich ein wenig mit Threads beschäftigt, oder? Selbst mir ist das mittlerweile klar und ich beschäftige mich erst seit einer Woche oder so mit dem Thema (und das auch leider nicht sehr intensiv).

    Was Du da beschreibst ist ja genau das, was mich wundert / was ich nicht gut finde. Also liegt es am Skill, der fehlt? Denn wenn man so was wie QT sowieso nutzt, braucht man nicht mehr als ein kurzes Hineinblicken in die Doku, um das vermeintliche Problem zu lösen - IMO. Vielleicht übersehe ich auch noch weitere Probleme?



  • Dann muss die ganze Geschäftslogik threadsicher sein. Wenn der GUI Thread blockiert ist und grundsätzlich alles im GUI Thread passiert, dann ist das ganze single threaded. Wenns nicht mehr im GUI Thread passiert, könnte der Benutzer was anderes anklicken und dann muss man überall für threadsicherheit sorgen und das ist dann wohl vielen zu umständlich. Grundsätzlich spricht aber natürlich nichts dagegen. Nur gibts es halt sehr viel Legacy Code, es gibt immer viel zu tun, und das alles umzubauen und zu testen hat halt meist nicht Prio A.



  • Es geht ja auch ganz billig 😉

    Bei QT einfach

    QCoreApplication::processEvents();
    

    in die Schleife der aufwendigen Berechnung setzen. Schon wird die Ereignisschlage der GUI wieder bearbeitet und die GUI verharrt nicht ohne Reaktion.



  • Hallo,

    Mann, was hast Du denn für Probleme? Wer oder was lässt Dich denn so lange warten? Was für lahme Kisten setzt Du denn ein?

    Mal eine andere Geschichte die dazu passt:

    Vor vielen Jahren (OK ich geb's zu es waren ein paar mehr) habe ich in meinem (damals) jugendlichen Leichtsinn ein Programm geschrieben um die Fakultät von 1000000 zu berechnen. Mich hat es einfach geärgert, daß mein Taschenrechner ab 79 nur noch Error geliefert hat.

    Auf das Ergebnis habe ich 7 Tage gewartet. Habe mal soeben spaßeshalber das gleiche mit meiner aktuellen Maschine ausprobiert. Was soll ich sagen: Das Ergebnis wurde schon geliefert, da hatte ich die Entertaste zum Starten des Programms noch nicht richtig losgelassen. Ich glaubte zuerst an einen Fehler und habe mir dann extra meine Aufzeichnungen von damals aus dem Archiv geholt und verglichen. Es war exakt das gleiche Ergebnis.

    Jetzt beschwer dich bloß nicht nochmal über Deine lahme Krücke. Du weißt nicht, was eine lahme Krücke ist.

    mfg Martin


  • Mod

    Hast du auch das gleiche Programm benutzt? Die Geschichte klingt nicht sehr glaubwürdig, kein Rechner sollte jemals mehrere Tage benötigen, um seinen gesamten Hauptspeicher mit signifikanten Stellen von 1000000! zu füllen. Eigentlich sollte sich der Zeitbedarf sogar so ungefähr die Waage halten über alle Epochen hinweg, da die Zeit wichtig ist, in der der Rechner Daten von der Länge seines Hauptspeichers verarbeiten kann. Und falls es nur auf kleine, feste Anzahl Stellen signifikant sein soll, schafft das sogar ein Computer aus den 80ern in Sekunden bis Minuten.



  • Vielleicht hat er den rekursiven Algorithmus aus der Mottenkiste geholt und der Compiler konnte ihn nicht auflösen.

    Edit: Wobei das Ding dann ne Menge Stack gebraucht hätte. 🤡



  • SeppJ schrieb:

    Hast du auch das gleiche Programm benutzt? Die Geschichte klingt nicht sehr glaubwürdig, kein Rechner sollte jemals mehrere Tage benötigen, um seinen gesamten Hauptspeicher mit signifikanten Stellen von 1000000! zu füllen. Eigentlich sollte sich der Zeitbedarf sogar so ungefähr die Waage halten über alle Epochen hinweg, da die Zeit wichtig ist, in der der Rechner Daten von der Länge seines Hauptspeichers verarbeiten kann. Und falls es nur auf kleine, feste Anzahl Stellen signifikant sein soll, schafft das sogar ein Computer aus den 80ern in Sekunden bis Minuten.

    So Du glaubst mir nicht?

    Nun gut ein paar mehr Infos:

    Am großen "Computer-Bild" Vergleichstest nahmen teil:

    • CBM 3032 - 6502 8-Bit CPU - 1 Kern - 1 Thread - 1 MHz Takt - 32 KB RAM - Commodore Basic - Baujahr ca. 1978
    • Dell Vostro 460 - i7-2600 64-Bit CPU - 4 Kerne - 8 Threads - 3,4 GHz Takt - 16 GB Ram - Borland C++ 5.02 - Baujahr 2011

    Das Programm hat natürlich nicht die Fakultät von 1000000 auf alle ca. 5,5 Mio Stellen berechnet, dazu wäre der alte Rechner ohne Festplatte gar nicht in der Lage gewesen.

    Den BASIC Quelltext von damals habe ich nicht mehr, den von gestern gibt es hier:

    #include <stdio.h>
    #include <math.h>
    
    int main( void )
    {
    	double			mantisse = 1.0;
    	unsigned long	exponent = 0;
    	long			i;
    	int				exp;
    
    	for( i=2; i<=1000000; i++ )
    	{
    		mantisse *= i;
    
    		exp = log10( mantisse );
    		if( exp )
    		{
    			exponent += exp;
    			mantisse /= pow10( exp );
    		}
    	}
    
    	printf( "%f %ld\n", mantisse, exponent );
    
    	return 0;
    }
    

    Mit den 7 Tagen, muß ich zugeben kann es schon sein, daß ich ein wenig übertrieben habe. Es ist ja schon viele Jahre her. Es waren aber auf jeden Fall mehrere Tage.

    mfg Martin



  • Die BilligeAntwort schrieb:

    Es geht ja auch ganz billig 😉

    Bei QT einfach

    QCoreApplication::processEvents();
    

    in die Schleife der aufwendigen Berechnung setzen. Schon wird die Ereignisschlage der GUI wieder bearbeitet und die GUI verharrt nicht ohne Reaktion.

    Wenn die Funktion das tut, was ich ihrem Namen nach vermute, handelt man sich überall Reentrancy-Probleme ein.


Anmelden zum Antworten