PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Java - Frage wg. Programmentwurf



White_Fox
22.07.2017, 20:37
Hallo

Ich bräuchte mal etwas Hilfe zwecks Softwareentwicklung. Es geht um eine Schaltung mit einem Mikrocontroller, der per FT232 mit einem Java-Programm redet. Jetzt geht es um das Java-Programm.

Ich will die direkte Kommunikation in einem eigenen Thread laufen lassen, das Datenaufkommen kann erheblich werden (ich will versuchen mehrere MBaud hinzukriegen, mal sehen wieviel geht). Dieser Thread hat nicht viel weiter zu tun als auf Daten zu warten, CRC berechnen und zurücksenden, Auszeit überwachen. Und das in beide Richtungen.

Reicht da sowas?


@Override
public void run(){
while(true){
//Hier die Kommunikationsauswertung/CRC-Prüfung/usw...
}
}

Ich will den Thread nicht 1ms (weniger geht anscheinend nicht) schlafen legen, das erscheint mir doch ein wenig viel. Allerdings muß der Thread im Leerlauf auch nicht die CPU voll auslasten.

Könnte das so funktionieren? Hat jemand noch andere Tipps für mich?
Das ist mein erstes "ernsthaftes" Java-Programm, mit Multi-Threading (und einigen anderen Dingen) hab ich mich in der Praxis noch nie beschäftigt, und doch hab ich da recht hohe Ansprüche an meine Arbeit.

Edit:
Ich hab grad etwas über Demonthreads gelesen. Ist es eine gute Idee, Threads mit solchen Aufgaben als Dämon laufen zu lassen? Wie geht das in Java? Ich hab das Runnable-Interface implementiert und nicht von der Thread-Klasse geerbt. Funktioniert das da auch?

shedepe
22.07.2017, 21:31
Deine Serielle Kommunikation dahinter läuft ungefähr mit 115,2 kbit/s (115200 Baud), je nachdem was du eingestellt hast (mehrere MBaud halte ich für unrealistisch weil den Mikrocontroller das auf der anderen Seite ja auch begrenzt)
. Nun angenommen du willst jedes Byte einzeln lesen und zwar möglichst zeitnah. D.h. du musst 115,2 / 8 = 14,4 kbyte / s lesen. Das sind pro Byte dann: 1/14,4 kbyte = 0,069 ms pro Byte bei voller Auslastung. Da hast du recht. Das schafft man nicht wenn man 1ms wartet ->
Nun hat die Seriellport Implementierung in Hardware und im Betriebssystem jedoch eine FIFO drin. D.h. du musst nicht jedes Byte in Echtzeit auslesen (Was auf einem normalen PC auch gar nicht möglich wäre) sondern dein PC speichert die Bytes zwischen und du kannst sie dann am Stück lesen und dann warten.

Alle Implementierungen die ich bisher dazu geschrieben habe (Zwar nicht in Java sondern in C++) haben zwischen 10 bis 50 ms zwischen zwei Abfragen gewartet.

Pseudo Code:


while(Abbruchbedingung)
{
while(Rs232Buffer != empty)
{
ReadByte(s)
}
Sleep(n - Ms)
}


Weniger als 1 ms warten geht nur wenn man selber aktiv wartet. D.h. man liest die High Precission Clock des Systems aus und zählt ticks.

Zum Thema Multithreading in Java kann ich dir leider nichts sagen. (Solltest du C++ nehmen wollen melde dich ;)

White_Fox
22.07.2017, 21:47
Danke... :)
Welcher einigermaßen Mikrocontroller ist denn bis 11kBd begrenzt? Selbst die ATMegas schaffen 2MBd...in diesem Fall werkelt da ein STM32, der schafft noch etwas mehr. :)

Die Daten kommen von der API übrigens über das Observer-Muster rein, an den RS232-Buffer komm ich gar nicht ran...und wie gesagt, ich will (in diesem Thread) nicht alle Bytes auslesen, sondern eigentlich (bisher) nur maximal drei bis vier sowie die Bytes eines gesamten Datenblocks (maximal 255) zählen. Und Millisekunden zählen.

Aber das soll hat fix und vor allem vom Rest des Programms unabhängig gehen.

shedepe
23.07.2017, 10:29
Mit einem STM32 würde ich direkt USB Bulk Transfers machen.
Außerdem wollte ich nicht sagen dass du keine 1-2 MBauds schaffst, sondern dass häufig mit geringeren Geschwindigkeiten geabeitet wird.

Welche API du hast habe ich keine Ahnung. Jede Serielport Implementierung die ich bisher gesehen habe (C/C++/C#/Python) ermöglicht es einem eine Abfrage zu machen: Wie viele Bytes sind da? und dann lies n-Bytes am Stück aufzurufen.

Letztendlich ist es ja genau das was du willst. Schauen wie viele Bytes sind da. Wenn deine API das nicht zulässt würde ich dir vorschlagen eine andere Implementierung zu suchen. In wirklich kurzen Zeitintervallen regelmäßig etwas zu machen ist auf einem normalen Desktoptechner nicht machbar allein scho nwegen des Schedulers.

White_Fox
23.07.2017, 11:12
Wie gesagt, von dem USB-Kram hab ich keine Ahnung. Damit befasse ich mich vielleicht später mal...

Die API die ich hab bietet zwar normale Lese- und Schreibmethoden, aber auch Observer-Benachrichtigung, und die benutze ich.

Schade...keine Java-Programmierer hier? Dabei ist die Sprache klasse, wie ich finde.

shedepe
23.07.2017, 15:43
Wie gesagt. Ich bin kein Java Programmierer, bin aber der Ansicht, dass die Sprache im Endeffekt recht egal ist. Ein Serialport bleibt ein Serielport egal ob man in C, Java oder Brainfuck anspricht. Manche Sprachen bauen Wrapper drumherum, die manchmal Sinn machen zu verwenden, manchmal auch nicht.

Nun lass uns deine Aufgabenstellung zerlegen. Du willst zum Einen das Observer Pattern verwenden, also benachrichtigt werden wenn neue Daten ankommen.
Die ersten Fragen die sich an dieser Stelle stellen:
1. In welchem Thread wird dieser Aufruf durchgeführt (main Thread, eigener Thread ?)
2. Wirst du für jedes Byte oder nur einen Satz an Bytes benachrichtigt -> Allgemein welche Informationen bekommst du an dieser Stelle

Diese Informationen können einem später weiterhelfen zu entscheiden ob ein extra Thread überhaupt Sinn macht.

Nun kommen wir weiter zu deiner Anforderung bzw. deinem ersten Vorschlag.
Du hast vorgeschlagen in einer Endlosschleife die Daten abzuprüfen. Hast du dir denn schon überlegt wie du die Daten an diese Stelle hinbekommst? (Ich habe dir an der Stelle einen recht klassischen Polling Ansatz beschrieben, deine Library oder das Betriebssystem macht untendrunter auch nichts anderes, wobei das Betriebssystem aber noch am ehesten die Möglichkeit hätte einen Interrupt basierten Ansatz umzusetzen)

Wenn du deinen Ansatz so verfolgen willst, wirst du nicht darum kommen die Daten selber zwischen zu speichern und zwar in einer Form, dass du gefahrlos aus zwei Threads darauf zugreifen kannst. Letztendlich hättest du dabei aber nur einen weiteren Zwischenspeicher eingeführt (eben in deiner eigenen Anwendung statt in der Seriallib bzw. im Betriebssystem)

Welche Schlüsse kann man daraus ziehen (bzw. die ich daraus ziehen würde): Wenn man den Event basierten Ansatz (also Observerpattern) verwenden willst, ist es meiner Ansicht nach am sinnvollsten direkt an der Stelle wo man benachrichtigt wird seine Bytes zu verarbeiten.

Wenn man einen extra Thread verwenden will ist es am sinnvollsten die Daten selber abzuholen. Wenn du das richtig machst, kannst du dir (nachdem was du beschrieben hast) eventuell sorgar teilweise sparen die Daten zu holen sondern musst nur abfragen wie viele Bytes gerade da sind.

Sollte man wirklich (auch dafür kann es gute Gründe geben) beide Ansätze kombinieren wollen, dann ist es das Sinnvollste eine Thread sichere Queue zu verwenden , dort von der Stelle wo man benachrichtigt wird welche Daten ankommen die Daten reinzuschreiben und in dem extra Thread die Daten aus der Queue zu holen und zu verarbeiten.

Welche Variante man davon verwendet ist meiner Ansicht nach, bei den Datenmengen die über eine Serielleverbindung laufen egal. Die Daten direkt in einem extra Thread zu holen hat meiner Erfahrung nach die geringste Latenz.

Ceos
24.07.2017, 06:30
Ich will den Thread nicht 1ms (weniger geht anscheinend nicht) schlafen legen, das erscheint mir doch ein wenig viel. Allerdings muß der Thread im Leerlauf auch nicht die CPU voll auslasten.



Thread.yield()

oder

try{
Thread.sleep(x);
} catch (InterruptedException ignore) {
}

zuerst einmal Thread.yield() das Allheilmittel gegen 100% CPU in einem Task der ohne Sleep arbeitet!




to (https://dict.leo.org/englisch-deutsch/to) yield (https://dict.leo.org/englisch-deutsch/yield) | yielded, yielded | (https://dict.leo.org/dict/flectionTable.php?kvz=4dkrADn71HeCbYCs67UP8lumz53R nXH3B-mrUdpdQIZTe1ZZGnQ9Em5XfxvCK9Jixu2d9SotFSHSa1UpisQ7 ksjXP35GLBcjANyN1_5d0mF3O9H4W8G7dAABzb1&lp=ende&lang=de)


nachgeben (https://dict.leo.org/englisch-deutsch/nachgeben) | gab nach, nachgegeben
(https://dict.leo.org/dict/flectionTable.php?kvz=4dkrADn71HeCbYC86wUP8lumz53R nXJXdznIcKs9Ymbk_2F424Jr9KvWOd6A7bUGR0nbEc99gbfCvZ cvLfUqAhtX_Gu1WwKEUd9_Rh7O5gGSfXYvznRORM6xLp2VLZfR EFzvJS9bAsHTKPF5PdLO4w3B_2ymTbMFdvlbiF3eEhDXnQf8Hd btJI2g_PxXvkShpykbBvsr1SF3O9H4jVQrd9AB-x5&lp=ende&lang=de)




Das bedeutet, dass der Task der gerade diesen Aufruf macht, dem System anbietet "Ich könnte jetzt Pause machen wenn du gerade etwas anderes zu tun hast"
Du hast keine 100% Auslastung, das ist ein Missverständniss, du hast 100% blockiert, weil du niemals Pause machst, sondern selbst das Abrufen eines leeren COM Port ohne unterlass wiederholst ohne dem System (oder dem CPU Core) die Möglichkeit zu geben auch mal andere Arbeiten auszuführen :)

Und das zweite was du bei einem Sleep beachten solltest, ein Sleep ist nicht zwangsläufig ununterbrechbar! Ein Sleep kann jedezeit utnerbrochen werden ohne dass die Daten verloren gehen, solange dein Input die MÖglichkeit hat deinen Schlafenden Thread zu wecken (Interrupt). Wie du deinen COM Port allerdings dazu bringst das Sleep zu Interrupten (Deswegen auch der Try-Catch-Block im Beispiel) müsstst du selber mal herausfinden, dafür efhlen mir zu viele Inofs :)

White_Fox
24.07.2017, 20:09
Ah...danke für die Erklärung der yield()-Methode. Ich hab da kurz was drüber gelesen, war mir aber nicht so sicher dabei. Ich denk das werd ich dann damit machen. :)

Und gut zu wissen daß auch die sleep()-Methode unterbrochen werden kann... :)