Wert in Variable schreiben, wenn diese gerade genutzt wird

Fohnbit

Top Contributor
Hallo!

Ich habe wohl ein Thread/Timing Problem?

Über 16 Methoden erhalte ich Boolesche Werte.
Ich halte diese Summe als Integer (16bit eben)

Wenn nun eine Methode einen Wert empfängt, update ich den Integer und warte mit der Weiterverarbeitung des Integer noch 50 Millisekunden (falls noch andere Methoden auslösen) und sende den Int Wert per UDP weiter.


Nun kann aber anscheinend passieren, das manche Werte von Methoden nicht übernommen werden. Ich vermute eben das ich ein Thread / Timing Problem habe, denn das passiert nur, wenn die Methoden fast zur selben Zeit Werte empfangen:

Methoden die auslösen:
Java:
sa = new snmpSA(getPropertyAddress(), getPropertyPort(),
         getPropertyCommunity(), this);

public void onChannel1(boolean value) {
        if (running)
            sa.sendChannel(1, value);
    }
usw. für die anderen 15 Methoden.
Running is nur aktiv, wenn die UDP Verbindung steht. Die ist fast immer "on".

Die Klasse snmpSA selbst:
Java:
    public snmpSA(final String host, final int port, String community,
            final NetworkListener networklistener) {
        executor = Executors.newSingleThreadScheduledExecutor();
        Address tHost = GenericAddress.parse(host + "/" + port);
        try {
            transport = new DefaultUdpTransportMapping();
        } catch (IOException e) {
            e.printStackTrace();
        }
        snmp = new Snmp(transport);
        try {
            transport.listen();
        } catch (IOException e) {
            e.printStackTrace();
        }
        target = new CommunityTarget();
        target.setCommunity(new OctetString(community));
        target.setAddress(tHost);
        target.setRetries(2);
        target.setTimeout(1500);
        target.setVersion(SnmpConstants.version1);
        this.networkListener = networklistener;

        hostAliveTimer = executor.scheduleAtFixedRate(new Runnable() {
            public void run() {
                snmpGet(EnumSAOID.Location.toString());
            }
        }, 20, 20, TimeUnit.SECONDS);

Und die Methode, die den Integer Wert aufbereitet:
Java:
public void sendChannel(int channel, boolean value) {
        if (value) {
            setSaStatus(saStatus | (1 << channel - 1));
        } else {
            setSaStatus(saStatus & ~(1 << channel - 1));
        }
        if (!busy) {
            busy = true;
            snmpSetValue(50);
        }

    }
Wenn also nicht busy, wird gesendet, ansonsten nicht.
Nach dem senden ist busy wieder false.

Da der Integer auch nach dem Senden nicht den Wert der Methoden angenommen hat, kanns nur so sein, dass die Methode "sendChannel(int channel, boolean value)" gar nie aufgerufen wird.

Optimal wäre es, wenn die Klasse zum senden der UDP Pakete ein eigener Thread wäre und die zentrale Integer Variable als gemeinsame Variable genutzt werden könnte.

Oder wie löst man sonst das Problem?

Vielen Dank
 
Ich schreibe ein Plugin ... die Methode wird überschrieben "@Override" und somit aufgerufen.
Diese Methoden Aufrufe sind schon 100.000mal vom Hersteller genutzt und da paßt sicher alles.

Daher bin ich mir sicher das die Methoden getriggert werden mit den Werten ... das Problem muss an meiner Architektur liegen, da wohl beim senden mit UDP meine Klassen blockieren(?)
 
Ich nehme an, es liegt an dieser Zeile bzw die im else-Zweig auch: "setSaStatus(saStatus | (1 << channel - 1))".
Sagen wir saStatus ist 0, und der erste Thread ist fertig, ruft also sendChannel() auf, ist nun in dieser Zeile und fragt zunächst den Wert von 'saStatus' ab. Dieser ist 0. Genau in desem Moment wechselt das Programm zu Thread 2, der auch gerade fertig wird und sendChannel() aufruft. Nun setzt er 'saStatus' z.b. auf 4. Wenn jetzt wieder zu Thread 1 gewechselt wird, rechnet er seine Rechnung nicht mit dem neuen Wert 4 weiter, sondern mit 0.
Ein 'synchronized' für die Methode 'sendChannel' sollte reichen.

lg Kevin
 
Hallo Kevin,

habe ich nun getestet und leider ist das Problem noch immer.
Was ich bemerkt habe:
Aus Eclipse auf meinem Win8 PC ist das Problem nicht.
Aber der Code läuft auf einem Mini Linux System ... da habe ich das Problem.
Scheint wohl ein Performance Problem zu sein.

Ich vermute wenn die Klasse die UDP Daten sendet, blockiert es den Empfang von neuen Daten, da der interne INT auch nicht aktualisiert wird.

Hätte jemand ein Beispiel wie man sowas aufbaut:
1. Methoden empfangen Boolean Wert
2. Zentraler Integer Wert muss aktualisiert werden
3. Wenn sich der Integer Wert ändert, soll nach 50ms der Integer per UDP gesendet werden
(jedoch soll die Main Klasse bei neu eintreffenten Werten die Integer auch aktualisieren und nach dem 1.Senden wieder 50ms senden

Wäre das eine Consumer/Producer Lösung?
Für Hilfe wäre ich echt dankbar!
 
Wenn du 16x ließt in 50ms, dann bist du bei unter 3ms pro Vorgang. Bei solchen "Echtzeit"-Anwendungen bist Du mit Java sehr schnell an der Grenze des möglichen.
 
Hallo,

die 16 Inputs werden so schnell wie möglich getriggert.
Wenn in Summe es 150ms dauert, werden eben mehrere UDP Befehle gesendet.

Der Grund, das ich jeden Bool Wert nicht einzeln mit SNMP sende ist, da ich UDP Telegramme reduzieren möchte und der Empfänger mit dem Integer ohne Verzögerung gleich alles ausführt.
Sende ich die UDP einzeln, merkt man eine höhere Verzögerung am Empfänger.

Daher möchte ich einfach 50ms warten, und dann alles senden, was bis dato eben schon da ist ... wenn danach neue Daten kommen, beginnt das Spiel von neuem.
 
Wäre es da nicht einfacher du sendest alle 50ms egal ob sich was geändert hat oder nicht? Was macht der Empfänger denn damit, dass er das Ergebnis 20x pro Sekunde braucht?
 
Das geht leider nicht, da das zuviel Buslast wäre und unnötig. Außerdem kann am Empfänger ebenso geschaltet werden (temporär) und würde die Manuelle bedienung überschreiben.

Ich denk ich erkläre es nochmal:
Der Empfänger ist ein Schaltausgang mit 16 Kanälen (ein/aus)

Wenn im ungünstigsten Falls alle 16 Kanäle eingeschaltet werden sollen, erhalte ich diese 16 Befehle einzeln. Es wird jeweils eine Methode getriggert mit dem Boolean Wert.

Ich würde also 16x den Boolean Wert per SNMP an dem Empfänger senden, damit dort die 16 Ausgänge einschalten.
Das ergibt eine sichtbare Verzögerung von etwa 1 Sekunde, bis Java alle gesendet und der Empfänger alle 16 SNMP Befehle ausgeführt hat.

"Schöner" ist es, wenn ich einen einzelnen Integer Wert an den Empfänger sende. Dieser eine Wert beinhaltet den Status aller 16 Ausgänge (Bitweise) und es werden gleich alle ohne Verzögerung geschaltet.

Damit ich nicht zu viele Integer Werte sende, warte ich einfach einige Millisekunden wenn ein neuer Boolean Wert ankommt. Eventuell kommen ja noch andere (100ms wären wohl auch denkbar).

Daher das Verzögerte senden, wenn sich der Integer ändert.
Ich bin mir ziemlich sicher das mein Code blockiert und daher Befehle nicht weitergesendet werden.

Könnte man die 16 einzelnen Befehle in eine Queue packen und diese beim ersten neuen Wert abarbeiten?
 
Ich habe mal etwas versucht:

Die Main Klasse:
Java:
protected BlockingQueue<Channel> queue = new ArrayBlockingQueue<Channel>(1024);

    public void init() {
        Consumer consumer = new Consumer(queue);
        new Thread(consumer).start();
    }

public void onChannel1(boolean value) {
        if (running)
        Channel channel = new Channel();
        channel.channel = 1;
        channel.value = value;

        try {
            queue.put(channel);
        } catch (InterruptedException e) {
                e.printStackTrace();
        }
    }
onChannelx eben für die anderen 15 auch

Der Consumer, der dann später auch die UDP Daten sendet:
Java:
import java.util.concurrent.BlockingQueue;

public class Consumer implements Runnable {

    protected BlockingQueue<Channel> queue = null;

    public Consumer(BlockingQueue<Channel> queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            while (true) {
                Channel channel = queue.take();
                System.out.println("Kanal: " + channel.channel + " Wert: "
                        + channel.value);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Die Channel Klasse:
Java:
public class Channel {
    public int channel;
    public boolean value;
}

Was ich mich nun frage:
Blockiert die Main Klasse, wenn es etwas in die Queue legen möchte, wenn die Queue gerade vom Consumer im Loop abgefragt wird?

Wäre das ein Ansatz um die Main Klasse "Blockierfrei" zu bekommen?

Danke!
 

Neue Themen


Zurück
Oben