Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   FastSwitch (https://www.delphipraxis.net/178190-fastswitch.html)

EWeiss 22. Dez 2013 10:41

FastSwitch
 
Hab da mehr oder weniger ein kleines problem mit meiner Library
Meine Plugins werden über einen Thread verwaltet und warten mit WaitForSingleObject 1 Sekunde lang bis das ThreadHandle zerstört wird.

Die Plugins können im VollBild mit den Tasten Up/Down vor oder zurück geschaltet werden.
Nun gibt es Leute die einfach nicht warten können und den Finger auf der Taste halten
das bedeutet das die Plugins dann innerhalb einer ms entladen und neu geladen werden.

Wie kann man das bewerkstelligen das in dieser zeit die DLL's ordnungsgemäß entladen/neugestartet werden können?
Ich für meinen Teil argumentiere damit das dies nicht der normale weg ist und einem Stresstest gleich kommt.

Gibt es da ne saubere Lösung zu?

gruss

Union 22. Dez 2013 10:52

AW: FastSwitch
 
Blockiere doch die Tastatur für die Wartezeit.

EWeiss 22. Dez 2013 11:15

AW: FastSwitch
 
Zitat:

Zitat von Union (Beitrag 1240742)
Blockiere doch die Tastatur für die Wartezeit.

Das wäre eigentlich ideal..
Ist das auch legitim? Was ist wenn die Anwendung aus unersichtlichen gründen abstürzen würde
dann ist die Tastatur blockiert oder?

Gibt es da irgendwo nähere Informationen?

Hmm eventuell mit BlockInput..
Lieber wäre mir dann nur die beiden Tasten zu sperren.
Aber ein Hook verursacht wieder AntiVirus Probleme .

gruss

Furtbichler 22. Dez 2013 11:34

AW: FastSwitch
 
Ich entprelle das einfach mit einem Timer (ca. 50-100ms). Der Tastendruck startet den Timer (immer wieder) neu.
Der Timer selbst lädt dann das Plugin. Das bedeutet im echten Leben, das ...
1. das einmalige Drücken der Taste mit 50-100ms Verzögerung zu einem Ergebnis führt (kaum bzw. gar nicht spürbar)
2. das kontinuierliche Drücken überhaupt nichts bewirkt und erst beim Loslassen mit der o.g. Verzögerung das Plugin geladen wird.

Alternativ speicherst Du deine Plugins zwischen.

EWeiss 22. Dez 2013 11:37

AW: FastSwitch
 
Zitat:

Zitat von Furtbichler (Beitrag 1240749)
Ich entprelle das einfach mit einem Timer (ca. 50-100ms). Der Tastendruck startet den Timer (immer wieder) neu.
Der Timer selbst lädt dann das Plugin. Das bedeutet im echten Leben, das ...
1. das einmalige Drücken der Taste mit 50-100ms Verzögerung zu einem Ergebnis führt (kaum bzw. gar nicht spürbar)
2. das kontinuierliche Drücken überhaupt nichts bewirkt und erst beim Loslassen mit der o.g. Verzögerung das Plugin geladen wird.

Alternativ speicherst Du deine Plugins zwischen.

Danke ist ne gute Idee
1 Sekunde für jedes Plugin sollte ausreichen.

Frohes Weihnachtsfest..

gruss

Furtbichler 22. Dez 2013 11:41

AW: FastSwitch
 
Eine Sekunde ist aber happig. Wenn der Ladevorgang zu lange dauert, dann müssen sie vorher geladen werden (imho). Aber ich hab ja die Anwendung nicht vor mir, kann das also nur aus der Ferne (grob) beurteilen.

Du machst das schon.

Frohes Fest zurück.

EWeiss 25. Dez 2013 03:07

AW: FastSwitch
 
Zitat:

Zitat von Furtbichler (Beitrag 1240752)
Eine Sekunde ist aber happig. Wenn der Ladevorgang zu lange dauert, dann müssen sie vorher geladen werden (imho). Aber ich hab ja die Anwendung nicht vor mir, kann das also nur aus der Ferne (grob) beurteilen.

Du machst das schon.

Frohes Fest zurück.

Wir haben es nun so gelöst..
Ob das elegant ist mag dahingestellt sein ;)

Code:
        case Action.ActionType.ACTION_PAGE_UP:
          {
            var timeSpamVerif = DateTime.Now - _lastAction;
            if (timeSpamVerif.TotalSeconds >= 2)
            {
              _validAction = true;
            }
            else
            {
              _validAction = false;
            }
            if (FullScreen && CurrentAudioStream != 0)
            {
              Log.Debug("BASS: Switch to Previous Vis");
              if (_validAction)
              {
                _lastAction = DateTime.Now;
                VizManager.GetPrevVis();
              }
            }
            break;
          }

        case Action.ActionType.ACTION_PAGE_DOWN:
          {
            var timeSpamVerif = DateTime.Now - _lastAction;
            if (timeSpamVerif.TotalSeconds >= 2)
            {
              _validAction = true;
            }
            else
            {
              _validAction = false;
            }
            if (FullScreen && CurrentAudioStream != 0)
            {
              Log.Info("BASS: Switch to Next Vis");
              if (_validAction)
              {
                _lastAction = DateTime.Now;
                VizManager.GetNextVis();
              }
            }
            break;
          }
wobei die 2 Sek. nicht variabel sind was wiederrum einen Deadlock auslösen könnte
wenn die Plugins zu schnell geändert werden da ich nur einen Thread zur Verfügung stelle.
Ist also Zeit abhängig und von PC zu PC unterschiedlich.
Hmmmm...

In der IDE funktioniert es mit 1 Sekunde als Eigenständige Ausführung dann nicht mehr deshalb 2 Sek.
Aber auch das könnte zu Problemen führen.
Was gäbe es da noch für eine Möglichkeit.

Timer wollen die nicht warum auch immer...

Und auf Änderung bzw. addieren eines KeyUp Events wurde gemeckert.
Was eigentlich die eleganteste und richtige Lösung wäre da man dann gezwungen wäre diesen erst loszulassen
damit das nächste Plugin geladen wird.

gruss

Namenloser 25. Dez 2013 03:23

AW: FastSwitch
 
Wie werden die Plugins denn geladen und entladen? Wenn jedes Plugin einfach eine zentrale Finalize-Routine hätte, die man vom Programm aus aufrufen kann, dann würde das Programn ja automatisch so lange blockieren, bis alles sauber aufgeräumt ist.

Bei statischen Wartezeiten bin ich immer sehr skeptisch... in 5 Jahren bremst die Konstante das Programm auf aktuellen Rechnern dann unnötig aus. Gleichzeitig kann es durch Paging usw. immer mal vorkommen, dass der Prozess doch länger braucht.

EWeiss 25. Dez 2013 03:29

AW: FastSwitch
 
Zitat:

Wenn jedes Plugin einfach eine zentrale Finalize-Routine hätte
Die habe ich ja BassVis_Quit.. bzw. BassVis_Free
Aber das nutzt überhaupt nichts wenn 1000 mal in einer sekunde versucht wird ein neues Plugin zu starten.

Wenn der Thread sich beendet werden gleichzeitig 100 neu aufgerufen..
Ich weis nicht wie ich das verhindern kann innerhalb meiner DLL.

Zitat:

Bei statischen Wartezeiten bin ich immer sehr skeptisch... in 5 Jahren bremst die Konstante das Programm auf aktuellen Rechnern dann unnötig aus. Gleichzeitig kann es durch Paging usw. immer mal vorkommen, dass der Prozess doch länger braucht.
Die weigern sich halt einen Eintrag für die Wartezeit in den Konfigurations Dialog einzutragen
mit dem Argument um so mehr Einstellungen eingefügt würden um so schlechter käme der User damit zurecht.
Für mich hört sich das an als würden die User für Dumm erklärt.

gruss

Namenloser 25. Dez 2013 03:45

AW: FastSwitch
 
Achso, dann verstehe ich das. Dann müsste es doch aber genügen, vor dem Aufruf der Finalize-Routine ein globales Flag auf False zu setzen und hinterher auf True. Und wenn die "Nächstes Plugin laden"-Methode aufgerufen wird, während das Flag False ist, dann bricht die Methode ab (oder wartet, bis das Flag wieder True ist). Natürlich noch threadsafe, das ganze. Läuft dann eigentlich auf ganz einfaches, normales Locking hinaus. Übersehe ich etwas?

EWeiss 25. Dez 2013 03:57

AW: FastSwitch
 
Zitat:

Zitat von Namenloser (Beitrag 1240973)
Achso, dann verstehe ich das. Dann müsste es doch aber genügen, vor dem Aufruf der Finalize-Routine ein globales Flag auf False zu setzen und hinterher auf True. Und wenn die "Nächstes Plugin laden"-Methode aufgerufen wird, während das Flag False ist, dann bricht die Methode ab (oder wartet, bis das Flag wieder True ist). Natürlich noch threadsafe, das ganze. Läuft dann eigentlich auf ganz einfaches, normales Locking hinaus. Übersehe ich etwas?

Werde das mal testen ;)
Normalerweise hab ich so was nicht gebraucht wenn die Anwendung die Lib Ordnungsgemäß behandelt.
Wie ein normal Mensch darauf kommen kann einfach den Button (taste) gedrückt zu halten ist mir unbegreiflich.

Danke.

EDIT:
Funktioniert leider nicht!
Wenn die so schnell umgeschaltet werden kommt mein Thread nicht mehr mit
Bei normalen gebrauch hingegen gibt es keine Probleme.


gruss

Furtbichler 25. Dez 2013 09:13

AW: FastSwitch
 
Wieso verwendest Du nicht einfach einen separaten Thread zum Laden des nächsten Plugins. Dieser Thread meldet, wenn er fertig ist und ein weiteres Plugin laden kann. Während der Thread beschäftigt ist, kann man eben nicht scrollen. Das ersetzt deine statische Konstante.

Wenn ich eine Liste von Dingern habe, bei denen ich nur alle 2 Sekunden weiterklicken kann, ist das -mit Verlaub- unzumutbar. Das sind doch bloß irgendwelche DLL, was soll daran 2 Sekunden dauern?

EWeiss 25. Dez 2013 09:48

AW: FastSwitch
 
Zitat:

Zitat von Furtbichler (Beitrag 1240980)
Wieso verwendest Du nicht einfach einen separaten Thread zum Laden des nächsten Plugins. Dieser Thread meldet, wenn er fertig ist und ein weiteres Plugin laden kann. Während der Thread beschäftigt ist, kann man eben nicht scrollen. Das ersetzt deine statische Konstante.

Wenn ich eine Liste von Dingern habe, bei denen ich nur alle 2 Sekunden weiterklicken kann, ist das -mit Verlaub- unzumutbar. Das sind doch bloß irgendwelche DLL, was soll daran 2 Sekunden dauern?

Genau das möchte ich verhindern.. Die Anwendung soll nicht Multithread fähig sein.
Also es soll und darf nicht mehr als ein Plugin zur gleichen zeit geladen werden.

Die meckern ja schon wenn die eine last von 20% CPU haben was soll das erst geben wenn man 2 gleichzeitig starten kann.

Mein Problem ist eigentlich ganz einfach zu erklären.
Der Thread ist nicht beendet wenn das neue Plugin startet.
bzw.. der nächste BeginThread aufgerufen wird.

Wie kann ich garantieren das dieser vorher 100% beendet wurde.

Zitat:

Das sind doch bloß irgendwelche DLL, was soll daran 2 Sekunden dauern?
Wenn man das so Pauschal sagen könnte ja, ist aber nicht der Fall

Ist es möglich das WaitForSingleObject Probleme macht?

Delphi-Quellcode:
    hEvent := CreateEvent(nil, True, False, nil);
    PostThreadMessage(RenderThreadId, WM_QUIT, 0, hEvent);
    WaitRe := WaitForSingleObject(hEvent, 1000);
    CloseHandle(hEvent);
im Thread signalisiere ich wenn er beendet wurde
Delphi-Quellcode:
  SetEvent(Msg.LParam);


Das trifft aber nicht rechtzeitig ein egal ob ich jetzt 1 oder 10 Sekunden übergebe


gruss

Sir Rufo 25. Dez 2013 10:39

AW: FastSwitch
 
Du wirst wohl eine Kontrollinstanz einsetzen müssen, die das Laden/Entladen der Plugins regelt.

Das könnte ja auch ein Thread sein.

Der weiß, welches Plugin geladen ist und stößt das Entladen an, wenn ein anderes Plugin geladen werden soll.
Sobald das alte Plugin entladen ist, wird das gewünschte Plugin geladen.
Dieser Plugin-Wunsch, kann sich während dieser Zeit ändern, sooft wie möglich, erst wenn das alte Plugin weg ist, dann wird dieser aktuelle Wunsch geladen.

Die Kontrollinstanz hätte z.B. folgende Zustände
Delphi-Quellcode:
( NoPlugin, LoadPlugin, UnloadPlugin, ReadyPlugin )
Während der Zustände
Delphi-Quellcode:
LoadPlugin
und
Delphi-Quellcode:
UnloadPlugin
würde einfach nur der Plugin-Wunsch vermerkt ohne eine Reaktion auszulösen.

In den Zuständen
Delphi-Quellcode:
NoPlugin
und
Delphi-Quellcode:
ReadyPlugin
bzw. beim Erreichen dieser Zustände, wird der aktuelle Wunsch mit dem aktuellen Plugin verglichen und im Bedarfsfall reagiert.

Furtbichler 25. Dez 2013 10:49

AW: FastSwitch
 
Der eine wartet mit 'WaitForSingleObject', der andere signalisiert (SetEvent, ReleaseSemaphore etc.), das der eine das Warten beenden kann.

Damit der Wartende zwischendurch andere Sachen machen kann, erlaubt der Aufruf von 'WaitForSingleObject' die Angabe einer maximalen Wartezeit. Der Rückgabewert ('WAIT_TIMEOUT'/ WAIT_OBJECT0') signalisiert, ob während des Wartens das Signal vom anderen gekommen ist, oder nicht, Ein Wert von -1 ('INFINITE') als maximale Wartezeit wartet eben so lange, bis das Handle geschlossen wurde ('CloseHandle') oder das Signal erfolgte.

Aber es ist auch klar, das hier zwei Threads am werkeln sein müssen: Einer, der wartet und einer, der signalisiert.

Zitat:

Zitat von EWeiss (Beitrag 1240981)
Genau das möchte ich verhindern.. Die Anwendung soll nicht Multithread fähig sein.
Also es soll und darf nicht mehr als ein Plugin zur gleichen zeit geladen werden.

Das hat mit Multithreading nichts zu tun. In einem Thread können blockierende/synchronisierte Aufrufe durchgeführt werden, ohne die Hauptanwendung zu blockieren. Ich will doch kein tolles schickes Sound-Tool haben, das zwischendurch weder auf Maus- noch auf Tastendrücke reagiert. Also: Laden des Plugins in einen Thread auslagern. Solange davon nur einer läuft, ist deine Forderung erfüllt, oder? Der Ansatz von Sir Rufo ist doch brauchbar.

Zitat:

...eine last von 20% CPU haben
Soweit ich mich erinnere, sind andere Implementierungen hier wesentlich weniger rechenintensiv. Ich glaube (bzw. behaupte in Unkenntnis der Details), Du machst hier etwas Grundlegendes falsch. Ich will Dir nicht zu nahe treten, aber bei der Frage nach der Parametrierung der Synchronisationsgeschichten ('WaitForSingleObject') und auch bei den selbsterdachten Hindernissen ('nicht Multithread fähig sein') scheint es mir, das Du das Optimierungspotential deiner Anwendung aufgrund vielleicht noch fehlender Programmierkenntnisse noch nicht ausgeschöpft hast.

EWeiss 25. Dez 2013 11:05

AW: FastSwitch
 
Zitat:

Soweit ich mich erinnere, sind andere Implementierungen hier wesentlich weniger rechenintensiv. Ich glaube (bzw. behaupte in Unkenntnis der Details), Du machst hier etwas Grundlegendes falsch. Ich will Dir nicht zu nahe treten, aber bei der Frage nach der Parametrierung der Synchronisationsgeschichten ('WaitForSingleObject') und auch bei den selbsterdachten Hindernissen ('nicht Multithread fähig sein') scheint es mir, das Du das Optimierungspotential deiner Anwendung aufgrund vielleicht noch fehlender Programmierkenntnisse noch nicht ausgeschöpft hast.
Keine sorge fühle mich nicht auf den Schlips getreten.
Zitat:

vielleicht noch fehlender Programmierkenntnisse
JO.. Wenn ich Profi wäre müsste ich nicht fragen ;)

Jeder wird irgendwann an seine grenzen stoßen.. aber wie gesagt kein Problem für mich da jetzt drauf rumzureiten.

Ich such nach einer Lösung die mit meinen Kenntnissen nicht so einfach umzusetzen ist.
So kann ich es auf Dauer jedoch nicht belassen.

Zitat:

Der Ansatz von Sir Rufo ist doch brauchbar.
Ist mir noch nicht ganz klar wie ich das umsetzen soll..
Kleines Beispiel ? Von mir aus auch mit Thread :)

Nebenbei mein Thread wird mit BeginThread erstellt (API)


gruss

Furtbichler 25. Dez 2013 11:18

AW: FastSwitch
 
:thumb: Emil. Schöne Antwort.
Mal ganz grob dahingerotzt. Der TPluginLoader ist ein Thread, der wartet, bis er ein Plugin (neu) laden soll. Das wird ihm mit einer Semaphore mitgeteilt (Du kannst auch ein Event nehmen). Derjenige, der jetzt ein neues Plugin will, ruft einfach 'LoadPlugin' auf. Über das Event 'OnSignalPluginLoaded' wird ihm dann mitgeteilt, wenn das Plugin geladen wurde. Der Aufrufer kann jetzt also andere Dinge erledigen, wie z.B. Maus/Tastendrücke verarbeiten oder Kaffeetrinken.


Delphi-Quellcode:
Procedure TPluginLoader.Execute;
Begin
  While not terminated do
    if WaitForSingleObject(fSyncHandle, INFINITE) = WAIT_OBJECT0 then begin
      thisPlugin := NextPlugin;
      UnloadPlugin(CurrentPlugin);
      LoadPlugin(thisPlugin);
      Synchronize(SignalPluginLoaded);
    end
    else Terminate := True; // Handle wurde wohl geschlossen
End;

Procedure SignalPluginLoaded;
Begin
  if Assigned (OnSignalPluginLoaded) then
    SignalPluginLoaded(self, CurrentPlugin);
End;

Procedure TPluginLoader.LoadPlugin(aPlugin : TPlugin);
Begin
  NextPlugin := aPlugin;
  ReleaseSemaphore(fSyncHandle,1,nil);
End;
Auf die Typdeklaration des Threads sowie weitere Schutzmechanismen zum Setzen/Abfragen des aktuellen/nächsten Plugins mit critical sections habe ich mich jetzt nicht gekümmert. Auch fehlt der code, der 'CurrentPlugin' setzt oder die von Rufo vorgeschlagene Statusänderung (die ich über Events publizieren würde).

Wenn Du damit (noch) nicht klarkommst, dann wird dir hier bestimmt weitergeholfen. Wichtig ist hier das Prinzip der Signalisierung/Synchronisierung nebenläufiger Prozesse über Synchronisationsobjekte (Mutexe, Events, Semaphoren) und Events (OnXXXX).

EWeiss 25. Dez 2013 11:30

AW: FastSwitch
 
Zitat:

Wenn Du damit (noch) nicht klarkommst, dann wird dir hier bestimmt weitergeholfen. Wichtig ist hier das Prinzip der Signalisierung/Synchronisierung nebenläufiger Prozesse über Synchronisationsobjekte (Mutexe, Events, Semaphoren) und Events (OnXXXX).
Und das ist womit ich mich noch nicht beschäftigt habe ;)
na ja ich kann jetzt nicht neue API's einbauen, denke aber das ich den Thread dann nur innerhalb meiner Function verketten muss.

Delphi-Quellcode:
procedure BASSVIS_ExecutePlugin(Param: PBASSVIS_EXEC; var Base: TBASSVIS_PARAM);
  stdcall;
wenn eine neue Anfrage kommt und der alte Thread noch nicht beendet wurde.
Danke für die Infos..


gruss

EWeiss 25. Dez 2013 12:16

AW: FastSwitch
 
komme da nicht mit klar muss da wohl passen.. :oops:
Aber trotzdem nochmal Danke für die Hilfe.

gruss

BUG 25. Dez 2013 20:48

AW: FastSwitch
 
Zitat:

Zitat von EWeiss (Beitrag 1240974)
Wie ein normal Mensch darauf kommen kann einfach den Button (taste) gedrückt zu halten ist mir unbegreiflich.

Das deutet darauf hin, das dass Navigieren durch die PlugIns umständlich ist. Ich würde das auch versuchen.

Um nochmal Furtbichlers ersten Vorschlag anzusprechen: Du solltest auf keinen die Benutzerschnittstelle verlangsamen; damit wirkt einfach dein Programm fehlerhaft.
Eher solltest du den Nutzer so schnell umschalten lassen, wie er möchte, und dann etwas verzögert das PlugIn nachladen (also wenn er sich "entschieden" hat).

EWeiss 26. Dez 2013 07:35

AW: FastSwitch
 
Zitat:

Zitat von BUG (Beitrag 1241036)
Zitat:

Zitat von EWeiss (Beitrag 1240974)
Wie ein normal Mensch darauf kommen kann einfach den Button (taste) gedrückt zu halten ist mir unbegreiflich.

Das deutet darauf hin, das dass Navigieren durch die PlugIns umständlich ist. Ich würde das auch versuchen.

Um nochmal Furtbichlers ersten Vorschlag anzusprechen: Du solltest auf keinen die Benutzerschnittstelle verlangsamen; damit wirkt einfach dein Programm fehlerhaft.
Eher solltest du den Nutzer so schnell umschalten lassen, wie er möchte, und dann etwas verzögert das PlugIn nachladen (also wenn er sich "entschieden" hat).

Mittlerweile hatte er mir ja einen anderen Vorschlag unterbreitet aber da komme ich nicht mit klar bzw. müsste dann extrem viel ändern da meine Threads auf API ausgelegt sind.

So schnell wie man dann die Plugins läd können diese sich gar nicht initialisieren.
Also der User würde in dem Fall gar nicht sehen was er da laden bzw. in dem Moment für ein Plugin erwischt.

Das ist auch der grund warum man unter Winamp diese aus einer Liste unter Konfiguration wählen muss.
Ein umschalten zur Laufzeit ist gar nicht möglich.

Werde da wohl dann nichts ändern wenn die Anwendung meinen Wrapper so missbrauchen will
dann ist es letztendlich nicht mein Problem da in diesem Fall die Handhabung nicht durchdacht ist.

gruss

Furtbichler 26. Dez 2013 10:18

AW: FastSwitch
 
Zitat:

Zitat von EWeiss (Beitrag 1241059)
Mittlerweile hatte er mir ja einen anderen Vorschlag unterbreitet aber da komme ich nicht mit klar bzw. müsste dann extrem viel ändern da meine Threads auf API ausgelegt sind.

Das geht mit reinen API-Calls genauso gut. Semaphoren bzw. Events bekommst Du mit einem API-Call, Events sind auch kein Geheimnis und wie man einen Thread mit 'BeginThread' startet, weißt Du ja auch. Die Idee dabei ist doch, das man die Arbeit von Threads untereinander synchronisieren muss und Windows alles bereit hält, um das sehr effizient umzusetzen.

Sir Rufo 26. Dez 2013 10:55

AW: FastSwitch
 
Zitat:

Zitat von Furtbichler (Beitrag 1241075)
Zitat:

Zitat von EWeiss (Beitrag 1241059)
Mittlerweile hatte er mir ja einen anderen Vorschlag unterbreitet aber da komme ich nicht mit klar bzw. müsste dann extrem viel ändern da meine Threads auf API ausgelegt sind.

Das geht mit reinen API-Calls genauso gut. Semaphoren bzw. Events bekommst Du mit einem API-Call, Events sind auch kein Geheimnis und wie man einen Thread mit 'BeginThread' startet, weißt Du ja auch. Die Idee dabei ist doch, das man die Arbeit von Threads untereinander synchronisieren muss und Windows alles bereit hält, um das sehr effizient umzusetzen.

Trotz alledem es einfach ist - wenn man es verstanden hat - steht vor dieser Einfachheit eben genau dieses Verstehen. Und da hat der TE ja explizit hingewiesen, dass dieses Verstehen noch nicht vorhanden ist.

Also wäre eine Erklärung oder Referenz angebrachter als der Hinweis, dass das geht und eigentlich recht einfach ist. ;)

Eine Erklärung / Referenz hätte ich auf die Schnelle aber auch adhoc nicht an der Hand

EWeiss 26. Dez 2013 11:38

AW: FastSwitch
 
Zitat:

Also wäre eine Erklärung oder Referenz angebrachter als der Hinweis, dass das geht und eigentlich recht einfach ist.
Danke Ja!

Aber ich kann nicht von irgend jemand erwarten das er für mich code schreibt.
Daher muss ich es im Moment leider so belassen auch wenn es mich stört.
Ich muss erst mal schauen ob es ein Beispiel im Net gibt wo ich das mal schritt für schritt beim debuggen verfolgen kann.
das ist meine Lernmethode ;)

Er hat mir ja freundlicher weise den weg gezeigt nur ich kann es nicht nachvollziehen weil es Anhaltspunkte sind.

Das ist mein Thread
Delphi-Quellcode:
function SoVisThread(glCtrl: PLongWord): DWORD;
//... nur nötiger teil
      repeat
        MsgReturn := GetMessage(Msg, 0, 0, 0);
        if ((Msg.message = WM_QUIT) or (Msg.message = WM_CLOSE)) then
          VisualizerQuitted := True
        else if Msg.message = DataReadyMsg then
        begin
          case Msg.wParam of
            DataReady:
              BassSoVis.SoRender(glCtrl^);
            RequestRestFlag:
              begin
                p1 := VisDataPointer;
                inc(p1, 70);

                p1^ := 0;
              end;
          end;
        end;

        TranslateMessage(Msg);
        DispatchMessage(Msg);
      until (integer(MsgReturn) <= 0) or VisualizerQuitted;
    end else
    PostMessage(BassSoVis.MessageHandle, DataReadyMsg, StartVisOut, 0);

  RenderThreadId := 0;

  Result := 0;
  ExitThread(0);

end;
Dieser wird beendet über
Delphi-Quellcode:
procedure TBASSSoVis.BassSonVisStop;
begin
  EndByProgram := True;

  if (RenderThreadId <> 0) then
  begin
    PostThreadMessage(RenderThreadId, WM_QUIT, 0, 0);

    repeat
      Sleep(15);
      WinProcessMessages;
    until RenderThreadId = 0;
  end else
  BassSoVisFree := True;

end;
gruss

Hier warte ich dann solange bis die RenderThreadId = 0 ist.
Also der Thread beim beenden diese auf 0 gesetzt hat.

nur beim schalten in Millisekunden Bereich funktioniert das nicht.
Auch das entfernen von Sleep(15) macht keinen unterschied.
Der Thread läuft noch während die anderen gestartet wurden.

gruss

Furtbichler 26. Dez 2013 17:16

AW: FastSwitch
 
Zitat:

Zitat von Sir Rufo (Beitrag 1241078)
Trotz alledem es einfach ist - wenn man es verstanden hat - steht vor dieser Einfachheit eben genau dieses Verstehen. Und da hat der TE ja explizit hingewiesen, dass dieses Verstehen noch nicht vorhanden ist.

Boah. Emil meinte doch nur, das er mit meinem Pseudocode ('TXYZ.Execute...Terminate etc.') nicht viel anfangen kann, weil er nicht von TThread ableitet, sondern alles direkt mit der API macht. Wenn man nun das weiß, was ich noch dazuschrieb (eben das man alles über die API abwickeln kann), reicht das doch als Hinweis, das es diesbezüglich ('ich verwende nur reine API calls') keine Hürde gibt.

@Emil: Anstatt per sleep und in einer Repeat/Until-Schreife zu warten, bis der Thread fertig ist, kann man das auch über ein Event lösen: Du sagst dem Thread 'Hör auf' (per Message, wie gehabt) und wartest einfach ('WaitForSingleObject'), bis der Thread als letztes das Event setzt ('SetEvent').

Mein Beispielcode zeigt ja nur einen 'Workerthread', der also einfach wartet, bis es etwas zu tun gibt. Du machst etwas ähnliches, und zwar, indem Du dem Thread Messages schickst. Deine Plugin-Threads würde ich auch gar nicht mehr anfassen wollen. Es geht ja hier um eine Konfiguration, die nicht ruckeln soll, wenn ein Plugin geladen/entladen wird.

Packe also deine Lade/Beendenlogik in einen Thread, der genau diese beiden Dinge tut (Altes Plugin entladen, Neues laden).

EWeiss 26. Dez 2013 17:35

AW: FastSwitch
 
Zitat:

@Emil: Anstatt per sleep und in einer Repeat/Until-Schreife zu warten, bis der Thread fertig ist, kann man das auch über ein Event lösen: Du sagst dem Thread 'Hör auf' (per Message, wie gehabt) und wartest einfach ('WaitForSingleObject'), bis der Thread als letztes das Event setzt ('SetEvent').
HI..
Jo genau das habe ich ja gemacht. Siehe hier

Delphi-Quellcode:
hEvent := CreateEvent(nil, True, False, nil);
PostThreadMessage(RenderThreadId, WM_QUIT, 0, hEvent);
WaitRe := WaitForSingleObject(hEvent, 1000);
CloseHandle(hEvent);

SetEvent(Msg.LParam);
Aber es nutzt nichts.

Das nächste Plugin wird dann schon gestartet bevor der Thread überhaupt signalisieren kann.
Bei normalen starts signalisiert das Event und der Thread wird rechtzeitig beendet.
Aber nicht wenn die Plugins im MS Bereich geändert werden.

Anwendung / Mein Wrapper mit dem Thread (DLL) Plugins werden dann damit geladen wenn von der Anwendung angefordert.
Die Anwendung fordert das so schnell an das mein Thread da nicht mehr mitkommt.

Sah dann so aus

Delphi-Quellcode:
procedure TBASSSoVis.BassSonVisStop;
var
  hEvent, WaitRe: DWord;
begin
   EndByProgram := True;

   if (RenderThreadId <> 0) then
   begin
     hEvent := CreateEvent(nil, True, False, nil);
     PostThreadMessage(RenderThreadId, WM_QUIT, 0, hEvent);
     WaitRe := WaitForSingleObject(hEvent, 1000);
     CloseHandle(hEvent);
     
     if WaitRe = WAIT_OBJECT_0 then
        RenderThreadId := 0;

   end else
   BassSoVisFree := True;

end;
Das einzige was jetzt noch sein könnte..
BassSonVisStop kein Teil vom aktuellen Thread ist.

So wird der Thread erstellt.
Delphi-Quellcode:
SOThreadHandle := BeginThread(nil, 0, @SoVisThread,
                                      @BassSoVis.glCtrl,
                                      0, RenderThreadId);

Zitat:

Es geht ja hier um eine Konfiguration, die nicht ruckeln soll, wenn ein Plugin geladen/entladen wird
Ich weis jetzt nicht wie du ruckeln definierst..
Wenn du die AV damit meinst dann magst du recht haben.



gruss

Furtbichler 26. Dez 2013 18:33

AW: FastSwitch
 
Zitat:

Zitat von EWeiss (Beitrag 1240981)
Delphi-Quellcode:
    hEvent := CreateEvent(nil, True, False, nil);
    PostThreadMessage(RenderThreadId, WM_QUIT, 0, hEvent);
    WaitRe := WaitForSingleObject(hEvent, 1000);
    CloseHandle(hEvent);
im Thread signalisiere ich wenn er beendet wurde
Delphi-Quellcode:
  SetEvent(Msg.LParam);
Das trifft aber nicht rechtzeitig ein egal ob ich jetzt 1 oder 10 Sekunden übergebe

Verstehe ich nicht. Was 'trifft aber nicht rechtzeitig ein'. Wenn ich das teste, klappt es:
Delphi-Quellcode:
Program Project2;

{$APPTYPE CONSOLE}

Uses
  SysUtils, Windows, messages;

Var
  WaitRe, RenderThreadId, hEvent: Cardinal;

Function SoVisThread(glCtrl: PLongWord): DWORD;
Var
  MsgReturn: LongBool;
  Msg: tagMSG;
  VisualizerQuitted: Boolean;

Begin
  VisualizerQuitted := False;
  Repeat
    write('Thread loop');
    MsgReturn := GetMessage(Msg, 0, 0, 0);
    If ((Msg.message = WM_QUIT) Or (Msg.message = WM_CLOSE)) Then Begin
      Writeln('*** close received. set Event ');
      VisualizerQuitted := true;
    End;
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  Until (integer(MsgReturn) <= 0) or VisualizerQuitted;

  SetEvent(hEvent);
  RenderThreadId := 0;
  Result := 0;
  Writeln('Thread finished');
  ExitThread(0);
End;

Begin
  hEvent := CreateEvent(Nil, True, False, Nil);

  BeginThread(Nil, 0, @SoVisThread, Nil, 0, RenderThreadId);
  Writeln('Thread created. Press ENTER to stop the thread');

  ReadLn;

  PostThreadMessage(RenderThreadId, WM_QUIT, 0, 0);
  Writeln('Close signalled, waiting for event');
  WaitRe := WaitForSingleObject(hEvent, INFINITE);

  CloseHandle(hEvent);
  writeln('Done (Press ENTER to terminate)');
  ReadLn;
End.
Ich habe das Event-Handle global deklariert, damit es vom Thread in jedem Fall verwendet wird und nicht nur dann, wenn es per Message übergeben wurde. Außerdem wartet 'WaitforSingleObject' jetzt solange, bis der Thread fertig ist.

EWeiss 26. Dez 2013 18:45

AW: FastSwitch
 
Danke für deine Arbeit..
Werde das mal testen und bescheid geben.

Ja so geht es bei mir auch ;)
Aber nicht wenn ich meinen Thread wenn er noch läuft in Millisekunden Takt erneut aufrufe.
PageUP/Down taste..


gruss

Furtbichler 26. Dez 2013 19:18

AW: FastSwitch
 
Zitat:

Zitat von EWeiss (Beitrag 1241110)
Aber nicht wenn ich meinen Thread wenn er noch läuft in Millisekunden Takt erneut aufrufe.
PageUP/Down taste..

Was meinst du mit 'erneut aufrufe?' Du meinst, wenn Du ihm Messages schickst? Das sollte kein Problem sein. Wenn ja, dann ist in deinem Messagehandler etwas faul.

EWeiss 26. Dez 2013 19:28

AW: FastSwitch
 
Zitat:

Zitat von Furtbichler (Beitrag 1241113)
Zitat:

Zitat von EWeiss (Beitrag 1241110)
Aber nicht wenn ich meinen Thread wenn er noch läuft in Millisekunden Takt erneut aufrufe.
PageUP/Down taste..

Was meinst du mit 'erneut aufrufe?' Du meinst, wenn Du ihm Messages schickst? Das sollte kein Problem sein. Wenn ja, dann ist in deinem Messagehandler etwas faul.

Wenn ich von der Anwendung in ms Takt ein neues Plugin zugewiesen bekomme das ich laden soll
habe ich genau das getan was du mir gezeigt hast siehe einige Post vorher.
Deshalb bin ich auch irgendwie jetzt leicht daneben warum ich immer noch im Thread herum werkle
während das nächste schon am laden ist.

Das Timing in Millisekunden funktioniert einfach nicht.
Das ist mein Problem.

Vielleicht versteht man mein Problem auch nicht richtig oder hab mich falsch ausgedrückt.
1. Start der Anwendung = 1 Plugin wird gestartet
2. PageUP wird gedrückt Plugin wird freigegeben über die API BASSVIS_IsFree
in Free wird nun mit BassSonVisStop der Thread gestoppt das Plugin entladen und die Resourcen freigegeben.
3. Während das Plugin in Free noch freigegeben wird kommt das nächste von der Anwendung über BASSVIS_ExecutePlugin
4. Ist bis dahin der Thread noch nicht beendet kommt es zu Problemen weil dann das Alte noch entladen wird.

VisInfo ist dann nicht mehr identisch mit dem das geladen/entladen wird.
Es kommt also unweigerlich zu Kuddelmuddel.
Bei normaler Verwendung geschieht das nicht nur wenn der Thread mit dem Timing nicht zurecht kommt.


gruss

Furtbichler 26. Dez 2013 20:24

AW: FastSwitch
 
Zitat:

Zitat von EWeiss (Beitrag 1241114)
2. PageUP wird gedrückt Plugin wird freigegeben über die API BASSVIS_IsFree
in Free wird nun mit BassSonVisStop der Thread gestoppt das Plugin entladen und die Resourcen freigegeben.
3. Während das Plugin in Free noch freigegeben wird kommt das nächste von der Anwendung über BASSVIS_ExecutePlugin
4. Ist bis dahin der Thread noch nicht beendet kommt es zu Problemen weil dann das Alte noch entladen wird.

Wieso wird "während das Plugin in Free noch freigegeben wird", "das nächste von der Anwendung über BASSVIS_ExecutePlugin" geladen? Genau da ist ja das Problem, d.h. Du musst eben in ExecutePlugin warten, bis Du laden kannst.

Entweder (am einfachsten) verweigert 'BASSVIS_ExecutePlugin' das Laden, solange ein Plugin im Speicher ist, dann muss der Aufrufer selbst dafür sorgen, das das so lange probiert wird, bis die Routine ein 'Ok' liefert.

Oder 'BASSVIS_ExecutePlugin' wartet selbst, bis kein Plugin mehr im Speicher ist.

Und das 'kein Plugin mehr im Speicher' erledigt deine Lade/Entladelogik über ein einfaches Flag.


PS: 'Timing' ist bei Multithreading immer gleichbedeutend mit Synchronisationsobjekten und nie mit 'Sleep'

EWeiss 26. Dez 2013 20:40

AW: FastSwitch
 
Zitat:

PS: 'Timing' ist bei Multithreading immer gleichbedeutend mit Synchronisationsobjekten und nie mit 'Sleep'
Ja! In meiner Verzweiflung habe ich alles versucht ;)
Man möge mir vergeben.. hehehehe


Zitat:

"das nächste von der Anwendung über BASSVIS_ExecutePlugin" geladen?
Das ist ja die frage?

Normalerweise geht man so vor das wenn man eine Taste drückt diese auch wieder loslässt.
Oder aber man geht hin und löst den Schaltvorgang erst dann aus wenn die Taste released (losgelassen) wurde.
In dem fall gäbe es dann auch überhaupt keine Probleme da bis dahin jedes Plugin entladen wäre.

Jetzt habe ich in diese Anwendung ein KeyUp Event eingebaut um genau das Problem auf dieser weise zu regeln.
Allerdings wurde dann rumgemault.

Ein Plugin mit OpenGL/Shadern und was nicht noch alles benötigt mindestens 2 Sekunden bis es entladen und seine resourcen freigegeben hat.
Wenn ich aber in dieser zeit Theoretisch 1000 neue innerhalb einer Sekunde lade kann irgendwas nicht funktionieren.
Jetzt sind die der Meinung das eine Anwendung im Stresstest das aushalten muss.

Nur wie will man da händeln?
Wenn also das neue läd während das alte noch nicht entladen ist und ich setze just in dem Moment VisInfo auf NIL
dann wirkt sich das nicht nur auf das alte sondern gleichzeitig auf das neue aus.

Damit habe ich zu kämpfen.

gruss

Furtbichler 26. Dez 2013 21:33

AW: FastSwitch
 
Mache es doch einfach so, das nicht 1000 gleichzeitig geladen werden, sondern maximal 5. Oder 3 oder 10.

Wenn nur das Entladen so lange dauert, ist das doch wurscht.

EWeiss 26. Dez 2013 22:02

AW: FastSwitch
 
Zitat:

Zitat von Furtbichler (Beitrag 1241124)
Mache es doch einfach so, das nicht 1000 gleichzeitig geladen werden, sondern maximal 5. Oder 3 oder 10.

Wenn nur das Entladen so lange dauert, ist das doch wurscht.

Ich denke du hast mir schon sehr geholfen..
Ich muss mal meinen ganzen repeat Mist rauswerfen und anstelle dessen mit Events arbeiten.


gruss

EWeiss 27. Dez 2013 00:06

AW: FastSwitch
 
Hab jetzt wieder stunden damit verbracht komme auf keinen Nenner. Da kann man nur eins zu sagen :wall:
Ich muss jetzt was pennen.

gruss

EWeiss 27. Dez 2013 16:03

AW: FastSwitch
 
Es scheint mir das die bisherigen Lösungen nicht zum ziel führen.

Delphi-Quellcode:
procedure TBASSSoVis.BassSonVisStop;
var
  WaitRe: Cardinal;
begin
  EndByProgram := True;

  if (RenderThreadId <> 0) then
  begin
    PostThreadMessage(RenderThreadId, WM_QUIT, 0, 0);
    hEventFree := CreateEvent(nil, True, False, nil);
    try
      repeat
        WaitRe := WaitForSingleObject(hEventFree, 15);
        if WaitRe <> WAIT_OBJECT_0 then
          WinProcessMessages;
      until WaitRe = WAIT_OBJECT_0;
    finally
      RenderThreadId := 0;
      BassSoVisFree := True;
    end;

  end else
  BassSoVisFree := True;

end;
WaitForSingleObject blockiert den Thread das ist aber nicht das was ich will
Rufe ich diese mit einer Wartezeit von 2000 auf dann blockiert/wartet der Thread bis diese zeit um ist.
wurde in dieser zeit das Event gefeuert (was aufgrund des Blockierten Threads nicht geht) wird mein Flag auf true gesetzt.
Das trifft aber niemals ein wenn das Event aus dem Thread abgefeuert wird. Also muss ein repeat her
und dann habe ich genau das was ich vorher auch hatte.

Dann kann ich genauso wie bisher Sleep(15) verwenden bleibt sich dann gleich.
Mit nur einem Thread ist das sinnlos es sei denn das Event wird wie in deinem Beispiel external also von außerhalb abgefeuert.

Und dort feuere ich ja quasi ein Event über BassVis_Free; (auch wenn es eine procedure ist)

gruss

Sir Rufo 27. Dez 2013 16:21

AW: FastSwitch
 
Liste der Anhänge anzeigen (Anzahl: 1)
Die grundsätzliche Idee dabei ist folgende:
  • Eine abstrakte Klasse
    Delphi-Quellcode:
    TPlugin
    (alle Methoden sind abstrakt)
  • Ein
    Delphi-Quellcode:
    TPluginGateway
    , dass von
    Delphi-Quellcode:
    TPlugin
    abgleitet ist und die Methodenaufrufe an das echte Plugin weitergibt, wenn es denn existiert
  • Ein
    Delphi-Quellcode:
    TPluginController
    (Thread) der für das
    Delphi-Quellcode:
    TPluginGateway
    das Laden/Entladen des Plugins steuert
  • Ein
    Delphi-Quellcode:
    TPluginLoader
    (Thread) der vom
    Delphi-Quellcode:
    TPluginController
    gestartet wird und entweder eine Plugin-Instanz erzeugt/lädt oder eine Plugin-Instanz zerstört/entlädt

Einzig der
Delphi-Quellcode:
TPluginController
benötigt ein Event und wartet einfach, bis was passiert ...
Delphi-Quellcode:
  procedure TPluginController.Execute;
    begin
      inherited;
      while not Terminated do
        begin
          // Warten bis etwas passiert
          FEvent.WaitFor;

          case State of
            csEmpty : // wenn leer, dann laden
              DoLoading;
            csLoading : // einfach mal nix
              ;
            csUnloading : // einfach mal nix
              ;
            csReady : // wenn bereit, dann entladen
              DoUnloading;
          end;
        end;
    end;
Im Anhang ein komplettes Projekt mit Source und Exe.

Ich verwende allerdings die Klassen der RTL und nicht native API Aufrufe (das sind im Grunde genommen nur Wrapper für die API Aufrufe).

EDIT Anpassungen für D2010

EWeiss 27. Dez 2013 16:25

AW: FastSwitch
 
Danke für deine Mühe werde mir das mal anschauen. ;)
Zitat:

Ich verwende allerdings die Klassen der RTL und nicht native API Aufrufe
Das ist kein Problem ob ich TThread oder API nehme kommt sich gleich.. mehr oder weniger ;)

Hmmm sehe schon!
Du verwendest XE ?

Zitat:

[DCC Fehler] PluginController.pas(30): E2137 Methode 'TerminatedSet' nicht in Basisklasse gefunden
[DCC Fehler] PluginController.pas(122): E2250 Es gibt keine überladene Version von 'WaitFor', die man mit diesen Argumenten aufrufen kann
[DCC Fataler Fehler] dp_178190.dpr(10): F1026 Datei nicht gefunden: 'Vcl.Forms.dcu'
Auch nach Änderung auf
Zitat:

uses
Forms,
geht es nicht kommen dann die anderen oben angezeigten Fehler

gruss

Sir Rufo 27. Dez 2013 16:50

AW: FastSwitch
 
Ich habe das angepasst und erneut hochgeladen (siehe letzter Beitrag)

EWeiss 27. Dez 2013 16:58

AW: FastSwitch
 
Zitat:

Zitat von Sir Rufo (Beitrag 1241205)
Ich habe das angepasst und erneut hochgeladen (siehe letzter Beitrag)

Danke ;)
Hatte es auch schon so weit allerdings macht es immer noch ärger.

[DCC Warnung] PluginGateway.pas(29): W1010 Methode 'Create' verbirgt virtuelle Methode vom Basistyp 'TPlugin'
>> constructor Create; reintroduce;

stürzt ab..
FEvent := TEvent.Create( nil, False, False, '' );

Delphi-Quellcode:
  constructor TPluginController.Create;
    begin
      inherited;
[DCC Fehler] PluginController.pas(56): E2008 Inkompatible Typen
>> inherited;

gruss


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:54 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz