Announcement

Collapse
No announcement yet.

Suspendieren von Threads

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Suspendieren von Threads

    An alle Experten wie Andreas Kosch, Hagen Reddmann ...

    Hallo!
    Ich habe ein Problem mit dem Suspendieren von Threads und finde weder im Forum, noch in Andreas’ Win32-Lösungen (Andreas, gutes Buch!) einen Hinweis. An welcher Stelle wird ein Thread bei MyThread.Suspend angehalten? Ist das Zufall?
    Die Arbeitsprozedur des Threads läuft, bis ich sie beende:

    procedure MyThread.Execute; // PseudoCode!!<br>
    begin<br>
    InitialisiereIrgendWas;<br>

    while not Terminated do // Wiederkehrende Tätigkeit <br>
    begin<br>
    Schritt_1;<br>
    Schritt_2;<br>
    ...;<br>
    Schritt_n-1;<br>
    Schritt_n;<br>
    end;<br>

    DeinitialisiereIrgendWas;<br>
    end;<br>

    Wenn ich im Hauptprogramm nun den Thread suspendiere (erst nach „InitialisiereIrgendWas“, also auf jeden Fall in der „while not Terminated“-Schleife), scheint es so, dass ich mich irgendwo befinde, NUR WO??? Ich möchte erreichen, dass die Schleife auf jeden Fall durchlaufen wird, ich mich also entweder bei/NACH „Schritt_n“ oder bei/VOR „Schritt_1“ befinde, bevor ich den Thread mit resume wieder starte. Geht das überhaupt?

    Please help!
    Vielen Dank!!!!

    Terry

  • #2
    Nein, ein mit Suspend gestoppter Thread stopt an der Stelle wo er sich gerade befindet. Das kann sogar mitten in einer genutzten Funktion sein. Willst Du einen "definierten" Suspendierungspunkt nutzten solltest Du folgendes testen:

    <pre>

    type
    TMyThread = class(TThread)
    private
    FSuspendPending: Integer;
    protected
    procedure Execute; override;
    public
    procedure SuspendPending;
    end;<br>

    procedure TMyThread.SuspendPending;
    begin
    InterlockedIncrement(FSuspendPending);
    end;<br>

    procedure TMyThread.Execute;<br>

    procedure CheckSuspend;
    begin
    if FSuspendPending > 0 then
    begin
    FSuspendPending := 0;
    Suspend;
    end;
    end;<br>

    begin
    while not Terminated do
    begin
    Schritt_1;
    CheckSuspend;
    Schritt_2;
    CheckSuspend;
    Schritt_3;
    CheckSuspend;
    end;
    end;<br>

    </pre>

    Obige Möglichkeit ist natürlich nur ein Ansatzpunkt und implementiert nur eine minimale Threadsynchronization/Locking. Sollte aber ausreichen da im schlimmsten Fall nicht sofort sondern erst im nächten Schritt gestopt wird.

    Gruß Hage

    Comment


    • #3
      Achso, der gestoppte Thread hält immer genau nach/in dem Suspend Aufruf. Ändere noch in:

      <pre>
      procedure CheckSuspend;
      begin
      if FSuspendPending > 0 then
      begin
      Suspend;
      FSuspendPending := 0; // wird nach einem Resume zurückgesetzt
      end;
      end;
      </pre>

      Der Code ist übrigens NICHT getestet, nur ein Vorschlag !

      Gruß Hage

      Comment


      • #4
        ... und noch 'ne Frage dazu: <br> <br>
        Der Execute-Teil hat einen Sleep(3000) Befehl. Wenn ich den Thread beende mit <br> <br>

        MyThread.Terminate; <br>
        MyThread.WaitFor; <br>
        MyThread.Free; <br>
        MyThread := nil; <br> <br>

        ist alles OK, der Thread wird erst gelöscht, wenn Execute komplett abgearbeitet wurde. Dies kann allerdings etwas dauern wegen dem langen Sleep(3000) und das Hauptprogramm stoppt leider an dieser Stelle. Was passie, wenn ich NICHT auf die Beendigung warte? Funktionieren tut's, aber gibt das Speicherleichen oder sonstige Probleme? Also: <br> <br>

        MyThread.Terminate; <br>
        MyThread.Free; <br>
        MyThread := nil; <br> <br>

        Oder soll ich den Thread besser vorher anhalten? Also: <br> <br>

        MyThread.Suspend; <br>
        MyThread.Terminate; <br>
        MyThread.Free; <br>
        MyThread := nil; <br> <br>

        Soll ich evt. FreeOnterminate := True, setzen? <br>

        Danach möchte ich den Thread sofort wieder erzeugen und starten, damit Execute wieder exakt am Anfang beginnt.
        <br> <br>

        Vielen Dank im Voraus <br> <br>
        Terr

        Comment


        • #5
          Hi

          Gute Fragen, da dieses "Termination" von Thread IMMER problematisch ist. Normalerweise ist es nicht von Bedeutung ob der Thread korrekt freibegeben wird da:<br>
          1.) API Threadresourcen, wie Threadhandle etc. durch Windows IMMER freigegeben werden wenn ein Process terminiert wird, egal wie er terminiert<br>
          2.) Da der Borland-Speichermanger intern auch nur API Resourcen alloziert werden diese ebenfalls zwangsweise durch das System freigegeben<br>

          Soweit die Theorie. Aber wie immer funktionierts nicht so richtig. Sollte der Thread z.B. Datenbanken geöffnet haben, COM's o.ä. könnten die durch externe Processe/Services zur Verfügung gestellt werden. Diese Processe haben nun nicht die Möglichkeit eine ungültige Termination eines Clientprocesses zu erkennen. Somit bleiben solch allozierte Resourcen OFFEN.

          Als einfachste Lösung für das Sleep() empfehle ich Dir folgendes:

          <pre>

          procedure TMyThread.Execute;<br>

          function DoSleep(Ticks: Integer): Boolean;
          const
          Interval = 100;
          begin
          Result := True;
          Ticks := GetTickCount + Ticks -Interval;
          while (GetTickCount < Ticks) and not Terminated do
          Sleep(Interval);
          if not Terminated then
          begin
          Sleep(Ticks + Interval - GetTickCount);
          Result := Terminated;
          end;
          end;<br>

          begin
          while not Terminated do
          begin
          Zeile_1;
          if DoSleep(30000) then Break;
          Zeile_2;
          if DoSleep(20000) then Break;
          end;
          end;

          </pre>

          Du splittest also die Gesammtsleepzeit in Teilstücke von "Interval" Millisekunden Länge auf. Der Code in DoSleep kostet dabei nur MINIMALE Rechenpower.

          Grundsätzlich sollte jeder Thread KORREKT beendet werden und seine Resourcen freigeben. Falls es im ermessen des Anwenders Deiner Anwendung steht, solche Threads abzuspalten, wäre es sinnvoll eine Hinweisbox bei der Beendignung zu bringen. Dieser Dialog sollte abfragen ob diese Threads nun beendet werden sollen, oder eben nur darauf hinweisen das JETZT die Anwendung "heruntergefahren" wird.

          Gruß Hage

          Comment

          Working...
          X