none
Frage zu Thread's - Anwendung in der Praxis RRS feed

  • Frage

  • Hallo

    ich hätte einmal eine Frage zur Anwendung von Threads in einer WinForm-Applikation:

    Vorweg -- ich bin noch nicht so sattelfest im Thema Thread, habe mich aber grundsätzlich viel eingelesen; nur daraus entsteht jetzt meine Frage -- dies zu einem Beispiel aus der Praxis:

    Ich habe eine Applikation, die als TCP-Server arbeitet und Daten in Empfang nimmt; es sind zwei Socket's dafür in Verwendung über unterschiedliche Ports, die Daten annehmen.

    Beide Sockets bekommen die gleiche Methode "WaitForData()", um dann die Daten weiter zu vearbeiten:

     public void WaitForData(Socket soc, int clientNumber)
            {
                try
                {
                    if (pfnWorkerCallBack == null)
                    {
                        pfnWorkerCallBack = OnDataReceived;
                    }
                    SocketPacket theSocPkt = new SocketPacket(soc, clientNumber);
                    soc.BeginReceive(theSocPkt.dataBuffer, 0,
                                    theSocPkt.dataBuffer.Length,
                                    SocketFlags.None,
                                    pfnWorkerCallBack,
                                    theSocPkt);
                }
                catch (SocketException se)
                {
                    SetMemoEditText(String.Format("{0} Error - SocketException WaitForData(): {1}", DateTime.Now, se.Message));
                }
            }

    Die Callback-Funktion sieht so aus: 

    public void OnDataReceived(IAsyncResult asyn)
            {
                SocketPacket socketData = (SocketPacket)asyn.AsyncState;
    
                try
                {
                    int iRx = socketData.CurrentSocket.EndReceive(asyn);
                    char[] chars = new char[iRx + 1];
                     Decoder d = Encoding.GetEncoding(850).GetDecoder();
    
                    d.GetChars(socketData.dataBuffer, 0, iRx, chars, 0);
    
                    String szData = new String(chars);
    ..........
    ......
    ....
                			// *** HIER THREAD starten ??? ****/
                                    // Daten - Weiterverarbeitung !!! - aber mit dem GUID-Prim.Key und nicht mit der Message-Number wie bisher!!!
                                    using (hl7Parser parser = new hl7Parser())
                                    {
                                        if (!parser.StartParseProcess(newHl7DataPrimaryKey))
                                        {
                                            // Error - zurück-liefern 
                                            Abgewiesen++;
                                            replyMessage = CreateReplyMessage(false, hl7MessageNumber);
                                        }
                                        else
                                        {
                                            Empfangen++;
                                            // ACK OK zurückliefern.
                                            replyMessage = CreateReplyMessage(true, hl7MessageNumber);
                                        }
                                        LetzteZeit = DateTime.Now;
                                    }
    
                              
    
    ............
    ......
    ...
    
    
                        
    
    
                        byte[] byData = Encoding.ASCII.GetBytes(replyMessage);
                        Socket workerSocket = (Socket)socketData.CurrentSocket;
                        workerSocket.Send(byData);
                    }
    
                    UpdateScreen();
    
                    WaitForData(socketData.CurrentSocket, socketData.ClientNumber);
    
                }
                catch (ObjectDisposedException)
                {
                    Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
                }
                catch (SocketException se)
                {
                    if (se.ErrorCode == 10054) // Error code for Connection reset by peer
                    {
                        m_workerSocketList[socketData.ClientNumber - 1] = null;
                    }
                    else
                    {
                        SetMemoEditText(string.Format("{0} - Socket-Exception: {1}", DateTime.Now, se.Message));
                    }
                }
            }

    Es geht jetzt genau um den Punkt der Daten-Weitervearbeitung (habe es im Code-Beispiel oben mit // *** HIER THREAD starten ??? **** markiert).

    Derzeit läuft alles in einem Thread....

    Die (hier sogenannte) Daten-Weitervearbeitung, fügt sämtliche empfangenen Daten in eine Datenbank ein (entweder Neu-Anlage von Datensätze, Update von bestehenden Datensätze, Delete usw.)

    Bringt es etwas hier die die Methode des hl7Parsers.StartParseProcess(...) in einem neuen Thread zu starten.

    Da hier vielen Daten auch ziemlich zeitgleich ankommen, würden das viele Threads (parallel) sein? Würde sich hier etwas gegenseitig blockieren? 

    Und wie ist das mit der Erstellung der "ReplyMessage" nach erfolgreichem StartParseProcess() oder fehlerhaften StartParseProcess() ?? - ich denke, dass müsste man dann via zwei Events auslösen (Event für erfolgreich, oder Event für Fehler), die vom Thread bzw. dann vom hl7Parser ausgelöst werden.

    Und ist die Verwendung der "using(...)"-Anweisung des hl7Parser dann noch möglich ?

    Ich weiß, viele Fragen -- aber vielleicht hat hier jemand schon die Erfahrung mit TCP-Daten-Kommunikations-Applikationen gemacht, und kann mir dazu bitte ein paar Tipps geben.

    Besten Dank schon mal & schönen Gruß

    Michael



    Michael Erlinger

    Mittwoch, 21. September 2016 07:08

Antworten

  • Hallo Michael,

    wenn Du einen Begin.../End... mit AsyncCallback verwendest, befindet Du Dich im End... bereits auf einem (Thread Pool) Hintergrund-Thread. Den kannst Du weiter nutzen, um weitere Aufgaben im Hintergrund laufen zu lassen. Nur Benachrichtigungen (wie UpdateScreen?) an die Benutzer Oberfläche müssen über einen Synchronisationskontext laufen (Control.Invoke, Dispatcher.Invoke).

    Rückmeldungen können über BeginSend / EndSend laufen um es weiter asynchron laufen zu lassen. Über (Auto-/Manual)ResetEvents kann man wiederum synchronisieren, wo immer es nötig ist.

    Da jede Kommunikation sein eigenes Portpaar bekommt, sollte es auf Kommunikationsebene kein Problem geben. Ob das auf Serverebene zu Blockierungen kommt, hängt mehr von dem Server selbst ab. Deadlocks, Race-Conditions sind generell immer möglich, wenn auf gleiche Daten parallel  zugegriffen wird.

    So ganz verstehe ich nicht wie der Parser dabei für Probleme bereiten sollte. Wenn er HL7 Meldungen auswerten soll, so kann er das im Thread tun und Ergebnisse/Rückgaben sowohl an die Steueroberfläche und ggf. auch an die Gegenstelle gesendet werden. (Ein kapselndes using sollte nur Ressourcen sicher freigeben, nicht aber das Verhalten verändern)

    Gruß Elmar

    P. S.: der Code sieht aus nach Async Client/Server Communication C#, schau Dir auch mal die asynchronen Varianten an: Socket Code Examples. Weitere kleine Beispiele findet man bei C# in a Nutshell Chapter 23: Asynchronous Methods.


    • Als Antwort markiert M.Erlinger Montag, 26. September 2016 09:18
    Mittwoch, 21. September 2016 12:36
    Beantworter

Alle Antworten

  • Hallo Michael,

    wenn Du einen Begin.../End... mit AsyncCallback verwendest, befindet Du Dich im End... bereits auf einem (Thread Pool) Hintergrund-Thread. Den kannst Du weiter nutzen, um weitere Aufgaben im Hintergrund laufen zu lassen. Nur Benachrichtigungen (wie UpdateScreen?) an die Benutzer Oberfläche müssen über einen Synchronisationskontext laufen (Control.Invoke, Dispatcher.Invoke).

    Rückmeldungen können über BeginSend / EndSend laufen um es weiter asynchron laufen zu lassen. Über (Auto-/Manual)ResetEvents kann man wiederum synchronisieren, wo immer es nötig ist.

    Da jede Kommunikation sein eigenes Portpaar bekommt, sollte es auf Kommunikationsebene kein Problem geben. Ob das auf Serverebene zu Blockierungen kommt, hängt mehr von dem Server selbst ab. Deadlocks, Race-Conditions sind generell immer möglich, wenn auf gleiche Daten parallel  zugegriffen wird.

    So ganz verstehe ich nicht wie der Parser dabei für Probleme bereiten sollte. Wenn er HL7 Meldungen auswerten soll, so kann er das im Thread tun und Ergebnisse/Rückgaben sowohl an die Steueroberfläche und ggf. auch an die Gegenstelle gesendet werden. (Ein kapselndes using sollte nur Ressourcen sicher freigeben, nicht aber das Verhalten verändern)

    Gruß Elmar

    P. S.: der Code sieht aus nach Async Client/Server Communication C#, schau Dir auch mal die asynchronen Varianten an: Socket Code Examples. Weitere kleine Beispiele findet man bei C# in a Nutshell Chapter 23: Asynchronous Methods.


    • Als Antwort markiert M.Erlinger Montag, 26. September 2016 09:18
    Mittwoch, 21. September 2016 12:36
    Beantworter
  • Hello Elma

    vielen Dank für Deine Rückmeldung.

    Nein, ich habe auch kein aktuelles Problem damit -- ich wollte jetzt nur mal via CodeReview die Applikation überarbeiten, und dachte mir dass hier vielleicht Verbesserungen bei Anwendung von Thread-Technologie möglich wären.

    Habe mich zum Thema Threads in C# mal grundsätzlich eingelesen, und weil in dieser Applikation nichts von dem verwendet wurde, dachte ich hiermit etwas verändern zu müssen.

    Aber wenn es (wie von Dir beschrieben) schon damit im Threadpool läuft, dann passt es.......

    Danke & schönen Gruß

    Michael


    Michael Erlinger

    Montag, 26. September 2016 09:18