SDA SE Wiki

Software Engineering for Smart Data Analytics & Smart Data Analytics for Software Engineering

User Tools

Site Tools


Differences

This shows you the differences between two versions of the page.

Link to this comparison view

research:jtransformer:threadingnotifyui [2018/05/09 01:59] (current)
Line 1: Line 1:
 +Die folgende Situation ist hinreichend bekannt, weil häufig anzutreffen: ​
 +  *Von der UI unabhängiger (z.B. auf Socket synchronisierter) Thread
 +  *Soll Events (oder Methodenaufrufe allgemein) an UI absetzen
 +  *UI Aufrufe müssen aber - s.o. - auf dem EPT der UI getätigt werden.Beispiel[[{java2htmlplugin
  
 +classsocketthreadextendsthread{
 +
 +..(...)
 +
 +publicvoidrun(){
 +shouldberunning=true;​
 +while(shouldberunning){
 +
 +..(...)
 +
 +..trytoreadamessagefromthesocket.do_not_block!
 +m=readmessage();​
 +
 +..notifylistenersifsuccsessfull
 +if(m!=null&&​shouldberunning){
 +firemessagerecieved(m);​
 +}
 +
 +..benicetootherthreads
 +try{
 +sleep(100);
 +}
 +catch(interruptedexceptionie){
 +system.err.println("​clientthreadcouldnotsleep!"​);​
 + ie.printstacktrace();​
 +}
 +}
 +}
 +
 +..(...)
 +}|{java2htmlplugin
 +
 +classsocketthreadextendsthread{
 +
 +..(...)
 +
 +publicvoidrun(){
 +shouldberunning=true;​
 +while(shouldberunning){
 +
 +..(...)
 +
 +..trytoreadamessagefromthesocket.do_not_block!
 +m=readmessage();​
 +
 +..notifylistenersifsuccsessfull
 +if(m!=null&&​shouldberunning){
 +firemessagerecieved(m);​
 +}
 +
 +..benicetootherthreads
 +try{
 +sleep(100);
 +}
 +catch(interruptedexceptionie){
 +system.err.println("​clientthreadcouldnotsleep!"​);​
 + ie.printstacktrace();​
 +}
 +}
 +}
 +
 +..(...)
 +}]]
 +Beim absetzen der Events gibt es dann evtl ein Problem:<​Code>​
 +  private void fireMessageRecieved(MessageEvent m) {
 +    Vector cloned = null;
 +    synchronized (listeners){
 +      cloned = (Vector)listeners.clone();​
 +    }
 +    for(int i=0;​i<​cloned.size();​i++){
 +      MessageListener l = (MessageListener )cloned.get(i);​
 +      l.messageRecieved(m);​ //BANG! (if listener is not thread safe)
 +    }
 +  }
 +
 +  //(...)
 +}</​Code>​
 +
 +
 +
 +
 +
 +Wer schon mal mit JFC/Swing zu tun hatte kennt vermutlich die Lösung:
 +
 +<​Code>​javax.swing.SwingUtillities.invokeAndWait() // (blocking)</​Code>​
 +bzw <​Code>​javax.swing.SwingUtillities.invokeLater() // (non-blocking)</​Code>​
 +
 +Beide methoden erwarten als Argument ein Runnable, das dann auf dem AWT EPT ausgeführt wird. Entsprechend ​
 +gibt es auch für SWT die folgenden beiden Methoden:
 +
 +<​Code>​org.eclipse.swt.widgets.Display.syncExec() // (blocking)</​Code> ​
 +bzw <​Code>​org.eclipse.swt.widgets.Display.asyncExec() // (non-blocking)</​Code>​
 +Ähnlich wie bei den Swing Methoden oben stellen diese sicher, daß das übergebene Runnable auf dem zum jeweiligen ​
 +Display object gehörendem EPT aufgerufen wird.TODO //(Details zur Beziehung Display<​->​EPT !!!// Noch unvollständig aber ein Anfang: [[eventprocessingthread|eventprocessingthread]]
 +
 +Gerne würden man dieses Runnable als anonyme innere Klasse implementieren:​
 +
 +<​Code>​
 +  private void fireMessageRecieved(MessageEvent m) {
 +    Vector cloned = null;
 +    synchronized (listeners){
 +      cloned = (Vector)listeners.clone();​
 +    }
 +    for(int i=0;​i<​cloned.size();​i++){
 +      MessageListener l = (MessageListener )cloned.get(i);​
 +      (magicalyKnownDisplayInstance).asyncExec(new Runnable(){
 +         ​public void run(){
 +           ​l.messageRecieved(m);​ // Compiler will complain here!
 +         }
 +      });      ​
 +    }
 +  }
 +}</​Code>​
 +Geht aber nicht: Innerhalb des Runnables dürfte auf m nur zugegriffen werden wenn es ''​final'' ​
 +deklariert wäre. (Warum eigentlich??​)
 +
 +**Annonyme Klassen in Java haben nur Zugriff auf die final Variablen und Methodenparameter der umschließenden Methode,
 +da diese Variablen als implizite Parameter an den Konstruktor der anonymen Klasse übergeben werden und
 +in Feldern gespeichert werden.\\ ​
 + Dies ist notwendig, da Variablen innerhalb eines Methodenblocks
 +nach Ablauf der Methode wieder vom Stack genommen werden, also nach dem Methodenaufruf nicht mehr verfügbar wären.\\ ​
 +Sollten auch alle nicht final Variablen mit in diesen Prozess einbezogen werden,
 +wäre die Semantik einer Zuweisung nach der Objekterzeugung aber nicht klar.
 +Soll dann auch in der Instanz der anonymen Klasse das Feld neu gesetzte werden und wie?​\\ ​
 +Und das Threadsafe?​\\ ​
 +Um diese Problematik zu vermeiden darf man nur final Variablen referenzieren,​ was auch ausreicht.
 +Bezogen auf dein Beispiel kannst du einfach schreiben:​**
 +
 +<​Code>​
 +private void fireMessageRecieved(final MessageEvent m) {
 +...
 +(magicalyKnownDisplayInstance).asyncExec(new Runnable(){
 +public void run(){
 +l.messageRecieved(m);​ // Compiler will NOT complain here!
 +...
 +}</​Code>​
 +
 +**Deine Lösung ist natürlich möglich. Du hast hier explizt gemacht, was
 +der Compiler automatisch generieren würde, wenn du den Methodenparameter final machst!**
 +
 +
 +
 +
 +
 +
 +
 + Das wiederum geht häufig nicht, oder führt zu häßlichen Konstruktionen.
 +
 +//ok, es geht schon :-) Wenn ich jetzt so drüber nachdenke, ist es tatsächlich ziemlich gleiche wie mein Ansatz unten.// ​
 +Meine persönliche Lösung des Problems: Definiere eine innere Klasse, die Runnable um eine Winzigkeit erweitert: <​Code>​
 +private abstract class EventRunner implements Runnable{
 +  java.util.EventObject ev = null;
 +  public EventRunner(java.util.EventObject ev){
 +    this.ev = ev;
 +  }
 +}</​Code>​
 +
 +Die Methode run() implementiert man dann wie gehabt in einer anonymen Klasse:
 +
 +<​Code>​
 +      (...)
 +
 +      (magicalyKnownDisplayInstance).asyncExec(new EventRunner(m){
 +         ​public void run(){
 +           ​l.messageRecieved( (MessageEvent) ev); 
 +         }
 +      });      ​
 +
 +      (...)
 +   </​Code>​\\ ​
 +
 +
 +
 +
 +
 +
 +
 +**Anmerkung**Man beachte ''​(magicalyKnownDisplayInstance)''​ im obigen Beispiel! Die Frage //wo// (i.e. in welcher Klasse) die Entkoppelung stattfinden soll ist keineswegs trivial. Hier wäre es vermutlich sinnvoller gewesen, diese Aufgabe einem der UI näher stehendem Object anzuvertrauen,​ insbesondere einem //das weiß auf welchem (Display-)Thread//​ die Runnables ausgeführt werden müssen. Diskusion dieser Frage gibt's hier:​[[threadinguisyncresponsibility|threadinguisyncresponsibility]]
research/jtransformer/threadingnotifyui.txt · Last modified: 2018/05/09 01:59 (external edit)

SEWiki, © 2019