5

ist ein Followup auf meine Frage hier: Android thread runnable performanceAndroid synchronisiert onSensorChanged? Diese

Ich habe einige Schwierigkeiten Einwickeln meinen Kopf um synchronisierte Methoden für meine app

Ich bin Abfrage der Sensoren und Speichern der Sensorwerte in Arrays, wenn sie ändern

float[] accelerometerMatrix = new float[3]; 
float[] accelerometerWorldMatrix = new float[3]; 
float[] gyroscopeMatrix = new float[3]; 
float[] gravityMatrix = new float[3]; 
float[] magneticMatrix = new float[3]; 
float[] rotationMatrix = new float[9]; 

class InsertHandler implements Runnable { 
     public void run() { 
      //get values from arrays and insert into db 
     } 
    } 

public void onSensorChanged(SensorEvent event) { 
     sensor = event.sensor; 

     int i = sensor.getType(); 
     if (i == MainActivity.TYPE_ACCELEROMETER) { 
      accelerometerMatrix = event.values; 
     } else if (i == MainActivity.TYPE_GYROSCOPE) { 
      gyroscopeMatrix = event.values; 
     } else if (i == MainActivity.TYPE_GRAVITY) { 
      gravityMatrix = event.values; 
     } else if (i == MainActivity.TYPE_MAGNETIC) { 
      magneticMatrix = event.values; 
     } 

     long curTime = System.currentTimeMillis(); 
     long diffTime = (curTime - lastUpdate); 

     // only allow one update every POLL_FREQUENCY. 
     if(diffTime > POLL_FREQUENCY) { 
      lastUpdate = curTime; 

      //insert into database in background thread 
      executor.execute(insertHandler); 
     } 
    } 

alle 10ms, nehmen meine App die aktuellen Sensorwerte (aus den Arrays) und fügen sie sie in eine Datenbank einen einzigen Thread Testamentsvollstrecker verwenden. Also schreibt die onSensorChanged Methode sowohl in die Arrays, als auch liest aus den Arrays in die Datenbank

Meine Frage ist, sollte die onSensorChanged Methode synchronisiert werden?

Das Wichtigste ist, dass ich keine Daten verpasse. Alle 10ms muss ich die aktuellen Sensorwerte speichern - keiner kann übersehen werden.

Nach meinem Verständnis bedeutet eine synchronisierte Methode, dass der UI-Thread eine Sperre hält und die Sensorwerte in die Arrays schreibt. Während dieser Zeit kann der Executor-Thread aufgrund der Sperre nicht von diesen Arrays lesen. Dann wird die Sperre aufgehoben und der Executor-Thread wird dann gesperrt, liest aus Arrays und schreibt in die Datenbank, löst Sperre

Ich könnte die Verwendung von synchronisierten Methoden hier missverstehen, vor allem gegeben onSensorChanged ist ereignisgesteuert und ich bin mir nicht sicher wie das in es spielt

Aber es scheint, dass in einer Situation wie dieser, ich nicht die neuesten Werte alle 10ms einfügen werde. Wenn der Benutzeroberflächenthread eine Sperre festlegt, kann der Executor-Thread diese Werte nicht in die Datenbank schreiben. Zu dem Zeitpunkt, zu dem der Executor-Thread schreiben kann, wären die Werte nun einige ms alt und ungenau Gleichzeitig wird der Executor-Thread halb geänderte Werte in die Datenbank einfügen

Also für diese Art von Situation, wo ich die neuesten/genaue Sensordaten alle 10ms einfügen muss, sollte ich eine synchronisierte Methode verwenden?

Antwort

2

Ihr aktueller Code ist nicht Thread-sicher, da die Runnable dieselben Arrays verwendet, in die der UI-Thread schreibt. Sobald Sie executor.execute(insertHandler); aufrufen, gibt es keine Garantie, dass der UI-Thread kein anderes Sensorereignis erhält und einen der Array-Werte ändert, bevor der Runnable diese in die Datenbank schreibt. Es scheint, dass Sie diesen Teil verstehen.

Um dies zu beheben, würde ich nicht empfehlen, einen synchronisierten Block überhaupt zu verwenden, da es scheint, dass Sie einfach alle Werte in den Arrays gespeichert werden möchten, wenn . Die onSensorChanged(...)-Methode selbst wird immer nur auf dem UI-Thread in Ihrem Code aufgerufen, so dass Sie sich keine Sorgen darüber machen müssen, dass ein anderer Thread den Wert der Arrays in dieser Methode ändert.

Mit all dem gesagt, was Sie tun können, speichert die aktuellen Werte der Arrays in einer neuen Instanz Ihrer Runnable Klasse. Ich weiß, dass es in Ihrem vorherigen Post vorgeschlagen wurde, die gleiche Instanz zu verwenden, aber das wird keinen spürbaren Unterschied machen. Sie können dies sogar überprüfen, indem Sie Android Monitor öffnen und die Speicherbelegung überprüfen, während Ihre Anwendung ausgeführt wird.Durch das Speichern der aktuellen Werte ist es nun egal, ob onSensorChanged() erneut aufgerufen wird, bevor Sie die Daten schreiben, da Sie bereits eine Kopie der Daten haben, die Sie nicht ändern. Hier

ist, was ich in Code bin darauf hindeutet:

class InsertHandler implements Runnable { 
    final float[] accelerometerMatrix; 
    final float[] accelerometerWorldMatrix; 
    final float[] gyroscopeMatrix; 
    final float[] gravityMatrix; 
    final float[] magneticMatrix; 
    final float[] rotationMatrix; 

    public InsertHandler(float[] accelerometerMatrix, float[] accelerometerWorldMatrix, 
      float[] gyroscopeMatrix, float[] gravityMatrix, 
      float[] magneticMatrix, float[] rotationMatrix) { 
     this.accelerometerMatrix = accelerometerMatrix; 
     this.accelerometerWorldMatrix = accelerometerWorldMatrix; 
     this.gyroscopeMatrix = gyroscopeMatrix; 
     this.gravityMatrix = gravityMatrix; 
     this.magneticMatrix = magneticMatrix; 
     this.rotationMatrix = rotationMatrix; 
    } 

    public void run() { 
     // use class field arrays values and insert into db 
    } 
} 

Und dann, wenn Sie die Runnable zum executor Verwendung hinzufügen:

Runnable insertHandler = new InsertHandler(accelerometerMatrix, accelerometerWorldMatrix, 
     gyroscopeMatrix, gravityMatrix, magneticMatrix, rotationMatrix); 
executor.execute(insertHandler);