Wieso lassen so viele Programme den Benutzer so lange warten?
-
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
-
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.
-
µ schrieb:
Wenn die Funktion das tut, was ich ihrem Namen nach vermute, handelt man sich überall Reentrancy-Probleme ein.
Das sieht auch für mich wie das Pendant zum berüchtigten Application.DoEvents() aus
Ein Hoch auf die gefakte Parallelität.
-
GPC schrieb:
µ schrieb:
Wenn die Funktion das tut, was ich ihrem Namen nach vermute, handelt man sich überall Reentrancy-Probleme ein.
Das sieht auch für mich wie das Pendant zum berüchtigten Application.DoEvents() aus
Ein Hoch auf die gefakte Parallelität.
Genau das.
Mein Vorgänger hat überall Application.DoEvents() verwendet. Auch in DLLs zur Hardwareansteuerung. Ich bin diesem Menschen nie begegnet und trotzdem hasse ich ihn
-
µ schrieb:
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.
Ich habe es gerade getestet.
Ich weiß nicht wieso es so funktioniert aber ein auslösendes Signal startet eine Methode die sich noch in einer Berechnung befindet erst dann erneut, wenn sie abgearbeitet ist bei Qt. Es wird auch maximal ein weiter Aufruf gespeichert.Wenn also während der Ausführung ein Signal entsteht dass die selbe Methode nochmal aufgerufen werden soll, passiert so lange nichts, bis die Methode abgearbeitet ist. Der Aufruf findet erst danach statt. Aber auch nur einmal, egal wie viele Signale inzwischen produziert worden sind.
Ich habe es mit Qt 4.7.4 getestet.
-
Nachtrag:
Es scheint irgendwie betriebsystem abhängig zu sein. Nur bei Windows 7 habe ich keine Selbstaufrufe während die Methode noch läuft, bei Windows XP schon.
Also QCoreApplication::processEvents(); vorsichtig verwenden
-
Um nochmal auf das Grundlegende Thema zurück zu kommen:
Es bietet sich halt oft an, längere Operationen nicht in Asynchron auszuführen. Zum einen wie schon genannt, da es die Entwicklung vereinfacht, zum anderen aber auch, da es für den Benutzer am Ende egal ist, ob er die Anwendung nicht verwenden kann, weil sie für den Zeitraum nicht reagiert, oder weil die relevanten Steuerelemente gesperrt sind.