Jump to content

Threads in C#-Bindings


Recommended Posts

Die beiden Threads RecvLoop und CallbackLoop in der IPConnection-Klasse sind für das Auswerten der Rückantworten der Devices zuständig ?

Arbeiten diese parallel zum Hauptthread nur solange, bis die Devices hinzugefügt wurden und werden abschließend beendet ? Oder bleiben beide suspended ?

Ich würde das Prinzip gerne verstehen, um es passend in Delphi zu portieren.

Link to comment
Share on other sites

Der RecvLoop läuft dauerhaft, liest alles was am Socket ankommt und behandelt es passend. Der CallbackLoop ist dafür da die Callbacks auszuführen, so dass man aus Callbacks heraus auch noch Getter der Devices aufrufen kann. Würden der RecvLoop die Callbacks ausführen würde dieser ja in der Zeit keine eingehenden Packages mehr annehmen können und wir hätten ein Deadlock, da die Antwort des Devices auf den Getter Aufruf nicht gelesen würde.

 

Das ganze kommuniziert miteinander über thread-sichere Queues.

Link to comment
Share on other sites

Der Thread des RecvLoop läuft dauerhaft ? Wenn ja was triggert den Einsprung ? Oder bewirkt das die interne Endlos-Schleife while(RecvLoopFlag) solange das Flag true bleibt ?

Wenn ich einen Breakpoint innerhalb der while Schleife setze, bleibt der Debugger nur dann stehen, wenn ich Devices hinzugefüge.

Link to comment
Share on other sites

Beide Threads laufen durch. RecvLoopFlag ist normalerweise True und wird beim destroy auf False gesetzt, damit man die Threads sauber abbrechen kann. Also steht da im Normalbetrieb ein while(True). Dass du im Debugger da nur was passieren siehst wenn du Devices ansteckst ist erwartet. Beim Anstecken schickt dir brickd eine Enumerate Message. Der socketStream.Read() Aufruf blockiert bis Daten zu lesen sind. Also steht der Thread die meiste Zeit in der Read() Methode wenn keine Daten übertragen werden.

Link to comment
Share on other sites

Zusammengefasst Thread RecvLoop agiert wie ein Listener bei Änderung des Device-Stacks und der Thread CallbackLoop wie ein Event-Handler bei Device-Rückantworten wie z.b. nach getter-Anfragen (GetCurrentPosition beim Stepper) ?

Link to comment
Share on other sites

Nein.

 

Der RecvLoop liest alles was auf dem Socket ankommt und ruft damit HandleMessage() auf. HandleMessage() unterscheidet dann verschiedene Messages:

 

- AddDevice Antworten werden ausgewertet und das entsprechende Device aktualisiert mit den Informationen über Name, Stack ID etc.

- Callback Antworten werden in die callbackQueue gesteckt

- Andere Nachrichten für bekannte Devices werden in deren entsprechenden answerQueues gesteckt

 

Der CallbackLoop nimmt Callback Antworten aus der callbackQueue und ruft die entsprechenden Callback Funktionen auf, falls solch eine vom Benutzer registriert wurde.

 

Getter Aufrufe für Devices warten maximal 2,5 sec bis in ihrer answerQueue etwas angekommen ist.

Link to comment
Share on other sites

Am Ende ist halt ausgeschlossen, dass der Receive-Thread an einer anderen Stelle blockiert als beim Lesen vom Socket. (Memo an mich/TF: was passiert wenn die BlockingQueue voll ist? Geht das?)

 

Woanders blockieren tut die Anwendung immer dann wenn mana uf die Antwort des Stacks wartet, solche Methoden können aber nur in dem Code vorkommen den der Library-User schreibt. Dieser Code wird nur von dessen eigenen Threads ausgeführt und eben vom Callback-Thread. Das ist ja am Ende der Thread der die "Auslieferung" der Callbacks durchführt.

Link to comment
Share on other sites

Verstehe ich das richtig, es gibt für das Socket kein OnReceive Event, das ihm sagt, jetzt lese aus ?

Der Thread bleibt beim Lesen solange stehen (Endlosschleife), bis das Stream.Read Daten lesen kann und evaluiert ?

Ist das also eine asynchrone Kommunikation ?

Link to comment
Share on other sites

Das ist nciht event-basiert sondern basiert auf blockierenden Funktionsrufen. Das heißt sobald du Stream.Read aufrufst blockiert dieser aufruf, das heißt die Ausführung des Threads wird unterbrochen (keine Schleife o.ä.) bis dort Daten anliegen. Sobald Daten vorhanden sind, geht die Ausführung ganz normal weiter. Das ganze ist also Synchron.

Link to comment
Share on other sites

Sorry, verstehe nur Bahnhof :(

Ich rufe explizit nicht stream.read auf sondern dies passiert im Thread RecvLoop, innerhalb der while-Schliefe. Der Thread wird instanziert und sofort gestartet, sobald IPConnection erzeugt wurde.

 

Irgendwann rufe ich in der Anwendung AddDevice auf, damit schreibe ich in den SocketStream.

Was und wann triggert dann in RecvLoop das im SocketStream gelesen wird ?

Solange dieser Thread nicht terminiert oder destroyed wird, bleibt der Thread aktiv und pollt im Stream ob er lesen kann ?

Link to comment
Share on other sites

Also ganz langsam und von vorn... So sieht im wesentlichen die Hauptschleife des Receive-Thread aus:

while(RecvLoopFlag) 
{
byte[] data = new byte[8192];
int length = socketStream.Read(data, 0, data.Length);

//mache irgendwas mit den gelesenen Daten
}

 

Das passiert in einem eigenen Thread.

Ich "debugge" jetzt mal:

while(RecvLoopFlag) 

RecvLoopFlag ist true, also gehe ich in den Schleifenrumpf

byte[] data = new byte[8192];

ich lege ein byte-array namens data an

int length = socketStream.Read(data, 0, data.Length);

Hier wirds spannend: Ich lese vom Stream, dabei ist spezifiziert, dass die Methode Read erst dann zurückkehrt (also erst dann gehts weiter), wenn mindestens ein Byte auf dem Stream lesbar ist.

Das heißt solange niemand etwas auf den Stream schreibt wird hier kein Code mehr ausgeführt, der Thread schläft. Sobald jemand auf den Socket schreibt bekommt dein Betriebssystem das mit, dieses weckt den Thread, sagt ihm, dass der IO auf den er wartet angekommen ist und der Thread macht genau dort weiter wo er aufgehört hat. Das heißt jetzt kehrt die Methode Read zurück, data ist befüllt und die Menge der gelesenen Bytes wurde zurückgegeben.

 

Ich hoffe jetzt war das einigermaßen klar... ansonsten sollten wir das villt mal ausskypen, weil villt ist der Knoten ja an einer ganz anderen Stelle ^^

 

Achso zum Lesen:

http://msdn.microsoft.com/en-us/library/system.io.stream.read.aspx

 

LG

Jan

 

P.S.: Mir ist dabei grad was aufgefallen... gibt nen neuen Thread ^^

Link to comment
Share on other sites

...dabei ist spezifiziert, dass die Methode Read erst dann zurückkehrt wenn mindestens ein Byte auf dem Stream lesbar ist.

Das heißt solange niemand etwas auf den Stream schreibt wird hier kein Code mehr ausgeführt, der Thread schläft. Sobald jemand auf den Socket schreibt bekommt dein Betriebssystem das mit, dieses weckt den Thread, sagt ihm, dass der IO auf den er wartet angekommen ist und der Thread macht genau dort weiter wo er aufgehört hat. Das heißt jetzt kehrt die Methode Read zurück, data ist befüllt und die Menge der gelesenen Bytes wurde zurückgegeben.

Genau der Block hätte gereicht. Den Rest und der zynische Unterton gehören hier nicht ins Forum.

Es ist durchaus möglich, das man als Anwend.entw. unterscheidet zw. eigener Implm. und der eingeb. Framework-API in der ich i.d.R. nix zu suchen habe. Insofern kann "sobald du Stream.Read aufrufst" ev. zu Irritationen führen.

Link to comment
Share on other sites

Da war kein zynischer Unterton mit dabei, also bleib mal locker... ich hatte nur wirklich keinen Plan was der Teil war den du nicht verstanden hast, also habe ich mich bemüht alles zu erklären.

 

Also ganz friedlich, okay? Und das mit dem Skype war auch ernst gemeint, wollte dich nicht angreifen oO

Link to comment
Share on other sites

Also ich bin in Hobby-Sachen immer locker ;) Skype ist nicht vorhanden, also alles andere ist obsolet... Wenn ich mich richtig erinnere gibt es non-blocking mode von Sockets, in diesem Fall würde der Thread am Read nicht warten sondern weiterlaufen ?

Link to comment
Share on other sites

Geben tut es sowas definitiv, aber wenn ich dich richtig verstehe willst du ja auf Delphi arbeiten, da weiß ich nicht was die Standardbibliothek auf welche Weise anbietet.

In C# funktioniert das beispielsweise mit dem Stream.ReadTimeout.

 

Bei C# würde dann eine Exception fliegen und du kannst als Entwickler damit umgehen (z.B. erstmal "was anderes machen" und später erneut lesen)

Link to comment
Share on other sites

Nein, ich meinte das nicht speziell auf Delphi bezogen, sondern prinzipiell wie das exemplarisch in C# zw. blocking und non-blocking unterschieden wird. C# kann ich eher lesen als Java oder Phyton, darum habe ich das C# Binding analysiert, allerdings waren die Socket-Sachen noch Neuland.

Jetzt ist das klarer für die Lsg. in Delphi.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...