Thread lässt Form etwas "hängen"



  • Hallo liebes Forum!

    hab ein ganz deftiges Problem. Wenn ich bei meinem IndyTCP Client einen Thread starte um das TCPClient Socket nach Daten abzuhorchen "hängen" alle Forms ein wenig nach. Damit meine ich das zwar jeder Buttonklick ausgeführt wird,es funktioniert wirklich alles,aber nicht visuel. Ein Button wird erst als "gedrückt" dargestellt wenn von meinem Thread gerade mal nicht Client->ReceiveBuff() aufgerufen wird. Die CPU Auslasung des Clients liegt aber bei 0 - max 3%. Der Thread läuft im TpNormal modus,also normale Prozesszeit. Wie kann ich die Abarbeitung meines Threads verlngsamen ohne Sleep() zu verwenden ?? oder kennt jemand eine Problemlösung ohne etwas an dem Thread zu verändern ?

    //---------------------------------------------------------------------------
    void __fastcall MyThread::Execute()
    {
        SetName();
    
        while(!Terminated)
      {
        Synchronize(Compute);
      }
    
    }
    //---------------------------------------------------------------------------
    void __fastcall MyThread::Compute()
    {
       Form1->ClientReceived(); //Funktion unter Form1 verarbeitet das IndyTCPSocket + GUI code
    }
    //------------
    


  • Hi,

    dass die Form hängt bei deiner Anwendung ist eigenlich kein Wunder, denn dur rufst aus dem Thread eine Funktion im Haupttrhead auf, und das in einer Endlosschleife, also wird ja auch der Hauptthread damit belastet. Um das zu verhindern solltest du meiner Meinung nach, deine Recieve funktion nur im Thread abarbeiten, und die empfangenen Daten mit Synchronize an den Hauptthread übergeben. Ansonsten würde ich sagen ist dein Versuch mit dem Thread ein wenig überflüssig, da deine Vorgehensweise nicht wirklich etwas bringt.



  • Die Lösung für das Problem sollte doch sein das ich den TThread als Background Thread verwende. Aber wie funktioniert das ? weiß da jemand weiter 😕



  • Danke Bufanda, ich werds mir durch den Kopf gehen lassen und die Lösung präsentieren. Ich denke daran den char Buffer im Thread header zu deklarieren und die Receive Funktion im Thread abzuarbeiten wie du sagst. Aber kann ich in Synchronize Funktionen verwenden die einen parameter übergeben ?



  • Wieso willst du Synchronize verwenden, wenn du keinen Datenaustausch mit deiner Form benötigst (wenn du, wie angegeben, die Daten im Thread deklarierst)?



  • Doch, ich benötige einen Datenaustausch mit der Form

    void __fastcall TForm1::ClientReceived()
    {
    
         if(Client1->Connected())
       {
           char Buff[1024];
           Client1->ReadBuffer(Buff,1024);
    
           if(strcmp(Buff,"TCP") == 0)
          {
              TCP_Message *msg = (TCP_Message *)Buff;
    
              if(strcmp(msg->Msg,"BYE") == 0)
             {
              Memo1->Lines->Add("Login data wrong");
             }
    
             else
              if(strcmp(msg->Msg,"HELLO") == 0)
             {
              Memo1->Lines->Add("Login data ok");
             }
    ......
    .............
    ....................
    


  • Ich verstehe nicht, warum du nen Thread benutzen willst, wo deine Form doch jedes mal vom Thread dazu gebracht wird, selber zu rechnen.
    Das führt doch den Thread ad absurdum...
    Warum machst du nicht den ganzen Kram, der in ClientReceived() steht, in dem Thread. Dann noch im Thread z.b. einen AnsiString Message und eine Methode AddStringToMemo().
    Diese rufst du per Synchronize immer dann auf wenn du etwas im Memo der Form ausgeben willst:

    void __fastcall MyThread::AddStringToMemo()
    {
         Form1->Memo1->Lines->Add(Message);
    }
    

    mfg
    xXx



  • -=]xXx[=- schrieb:

    Ich verstehe nicht, warum du nen Thread benutzen willst, wo deine Form doch jedes mal vom Thread dazu gebracht wird, selber zu rechnen.
    Das führt doch den Thread ad absurdum...
    Warum machst du nicht den ganzen Kram, der in ClientReceived() steht, in dem Thread. Dann noch im Thread z.b. einen AnsiString Message und eine Methode AddStringToMemo().
    Diese rufst du per Synchronize immer dann auf wenn du etwas im Memo der Form ausgeben willst:

    void __fastcall MyThread::AddStringToMemo()
    {
         Form1->Memo1->Lines->Add(Message);
    }
    

    mfg
    xXx

    Ok, das sehe ich ein. Nur gibts da noch ein kleines Problem:

    if(Client1->Connected())
       {
           Client1->ReadBuffer(Buff,1024);
    

    Das muss doch dann auch Synchronisiert werden ,oder ??



  • Nur wenn du TCPClient weiterhin auf der Form beläßt.
    Besser ist, den TCPClient dynamisch im Thread als Member zu erstellen.Meiner bescheidenen Meinung nach, sollte TCPClient, auch wenn es ein VCL-Objekt ist, dann zu deinem Thread gehören und unabhängig vom MainThread laufen.
    Für Zugriffe auf den TCPClient von deiner Form aus, solltest du dir entsprechende Methoden schreiben.

    Da ja der Thread zuständig ist, für das Lesen irgendwelcher Daten, sollte dies alles auch in seinem Zuständigkeitsbereich liegen.
    Grundgedanke:
    1. Form1 initialisiert den Thread und ruft eine Methode auf, die den TCPClient mit entsprechenden Parametern initialisiert.
    2. Der Thread rechnet vor sich hin...
    3. Wenn der Thread seine Aufgabe beendet hat (sofern dies möglich/vorgesehen ist) gibt er der Form über eine synchronisierte Methode bescheid...

    mfg
    xXx



  • -=]xXx[=- schrieb:

    Grundgedanke:
    1. Form1 initialisiert den Thread und ruft eine Methode auf, die den TCPClient mit entsprechenden Parametern initialisiert.
    2. Der Thread rechnet vor sich hin...
    3. Wenn der Thread seine Aufgabe beendet hat (sofern dies möglich/vorgesehen ist) gibt er der Form über eine synchronisierte Methode bescheid...

    Soweit hab ich es ja, das beenden des Threads wird bei Client disconnect ausgeführt (suspended,terminated). Das einzige was jetzt noch aus dem Thread herraus kontinuierlich unter Form1 aufgerufen wird ist der TCP Client. Ich werde versuchen das TCPSocket im Thread unterzubringen und berichten wie es lüppt



  • So, nun hab ich alle Indy Komponenten im Thread untergebracht und die Verbindung zum Server lässt sich aufbauen. Siehe da die Form hängt nicht mehr 😃 Aber da sind noch ein paar Fragen offen:

    //Threadheader:
    TIdTCPClient *Client;
    TIdSSLOptions *options;
    TIdSSLIOHandlerSocket *SSL;
    //---------------------------------------------------------------------------
    void MyThread::SetName()
    {
            THREADNAME_INFO info;
            info.dwType = 0x1000;
            info.szName = "MyThread";
            info.dwThreadID = -1;
            info.dwFlags = 0;
    
            Client = new TIdTCPClient(Form1);         //Warum Form1 und nicht this ??
            SSL = new TIdSSLIOHandlerSocket(Form1);
            Client->IOHandler = SSL;
    
            Synchronize(GetClientSettings); 
    
            __try
            {
            RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD),(DWORD*)&info );
            }
            __except (EXCEPTION_CONTINUE_EXECUTION)
            {
            }
    }
    //---------------------------------------------------------------------------
    void __fastcall MyThread::GetClientSettings()
    {
    
          index = Form1->ComboBox10->Text.Pos(":");
          if(index > 0 && index + 1 <= Form1->ComboBox10->Text.Length())
          {
            temp = Form1->ComboBox10->Text;
            port = temp.SubString(temp.Pos(":") + 1,temp.Length() - temp.Pos(":"));
            Client->Port = StrToInt(port);
            Client->Host = Form1->ComboBox10->Text.SubString(1,Form1->ComboBox10->Text.Pos(":") - 1);
    
            options = SSL->SSLOptions;
            if(Form1->ComboBox11->Text == "SSLv2")
            options->Method = sslvSSLv2;
            else
            if(Form1->ComboBox11->Text == "SSLv3")
            options->Method = sslvSSLv3;
            else
            if(Form1->ComboBox11->Text == "TLSv1")
            options->Method = sslvTLSv1;
          }
          else
          {
            Client->Host = Form1->ComboBox10->Text;
            Client->Port = 14888;
            Form1->ComboBox10->Text = Form1->ComboBox10->Text + ":14888";
    
            options = SSL->SSLOptions;
            if(Form1->ComboBox11->Text == "SSLv2")
            options->Method = sslvSSLv2;
            else
            if(Form1->ComboBox11->Text == "SSLv3")
            options->Method = sslvSSLv3;
            else
            if(Form1->ComboBox11->Text == "TLSv1")
            options->Method = sslvTLSv1;
          }
    
    }
    //---------------------------------------------------------------------------
    void __fastcall MyThread::Execute()
    {
         SetName();
         Client->Connect(5000);
    
         while(!Terminated)
         {
          ClientReceive();
          Synchronize(Compute);
    
          if(Client->Connected() == false)
          Terminate();
         }
    
    }
    //---------------------------------------------------------------------------
    void __fastcall MyThread::ClientReceive()
    {
      if(Client->Connected())
      {
       Client->ReadBuffer(Buff,1024);
      }
    }
    //----------------------------------------------------------------------------
    void __fastcall MyThread::Compute()
    {
    
           if(strcmp(Buff,"TCP") == 0) //--------Link Server Message
          {
              TCP_Message *msg = (TCP_Message *)Buff;
    
              if(strcmp(msg->Msg,"BYE") == 0)
             {
              Form1->Memo1->Lines->Add(String(Date()) + " " + String(Time())+" Login data wrong");
             }
    
             else
              if(strcmp(msg->Msg,"HELLO") == 0)
             {
              Form1->Memo1->Lines->Add(String(Date()) + " " + String(Time())+" Login data ok");
             }
    

    Wenn euch was böses auffällt dann sagt mir bitte Bescheid. Wie schreibt man sich z.B. die OnConnect, OnDisconnect events eines IndyTCPClients selbst ?


Anmelden zum Antworten