Wie synchronisieren Sie Threads auf der nativen Seite einer JNI-Umgebung ordnungsgemäß?

8

Fragen kurz

Ich verwende C ++ und Java in einem Prozess über JNI. Für den fraglichen Anwendungsfall greifen sowohl ein C ++ - Thread als auch ein Java-Thread auf dieselben Daten zu, sie tun dies auf der C ++ - Seite, und ich möchte den Zugriff ordnungsgemäß synchronisieren.

Bisher war fast meine gesamte JNI-Thread-Synchronisierung auf der Java-Seite, wo die Antwort offensichtlich ist: Verwenden Sie das bereitgestellte Java-Concurrency-Paket und die integrierten Sprachen für den gemeinsamen Zugriff. Leider ist die Antwort auf der C ++ Seite nicht so offensichtlich.

Was ich bisher versucht habe Kurz

Ich habe versucht, einen pthreads-Mutex zu verwenden, der denkt, dass es funktionieren könnte, obwohl ich keine pthreads verwende, um Threads zu erstellen, die aber gelegentlich beim Sperren hängen bleiben - ich werde ein Beispiel dafür weiter unten zeigen.

>

Fragedetails

In meiner derzeitigen, spezifischen Verwendung fragt c ++ nach Änderungen, die von Java mit einem 1-Sekunden-Timer geliefert werden (was ich nicht gerne hätte, aber ich bin mir nicht sicher, wie ich es ereignisgesteuert machen würde Legacy-C ++ - Code). Der Java-Thread stellt Daten bereit, indem eine native Funktion aufgerufen wird, und c ++ kopiert die Daten in eine C ++ - Struktur.

Dies ist die Art der Situation im Code (passiert bei 2 Threads, Thread1 und Thread2):

Codebeispiel

Bemerken Sie eine SSCCE, da es Definitionen für TheData und TheDataWrapper fehlt, aber es spielt keine Rolle, was sie enthalten. Angenommen, sie enthalten nur ein paar öffentliche int s, wenn das Ihrem Denkprozess hilft (obwohl es in meinem Fall mehrere Arrays von int und Arrays von float gibt).

C ++:

%Vor%

Java:

%Vor%

Was ich bisher versucht habe Details

Wenn ich versucht habe, pthread wie unten beschrieben zu sperren, bleibt die Ausführung manchmal in pthread_mutex_lock hängen. Es sollte in dieser Situation kein Deadlock geben, aber um weiter zu testen, habe ich ein Szenario ausgeführt, in dem supplyData überhaupt nicht aufgerufen wurde (es wurden keine Daten geliefert), also sollte kein Deadlock möglich gewesen sein, doch der erste Aufruf an poll wird gelegentlich trotzdem hängen. Vielleicht ist die Verwendung eines Pthreads-Mutex in dieser Situation keine gute Idee? Oder vielleicht habe ich etwas Dummes gemacht und es immer wieder übersehen.

Bisher habe ich versucht, Pthreads wie folgt zu verwenden:

Codebeispiel

C ++:

%Vor%

Eine andere Option, über die ich nachgedacht, aber noch nicht getan habe

Ich habe auch erwogen, JNI zu verwenden, um in Java zurückzurufen, um eine Sperre unter Verwendung des Java-Nebenläufigkeitssteuerelements anzufordern. Das sollte funktionieren, da jeder Thread bei Bedarf auf der Java-Seite blockieren sollte. Da der Zugriff auf Java von C ++ jedoch sehr ausführlich ist, hatte ich gehofft, diese Kopfschmerzen vermeiden zu können. Ich könnte wahrscheinlich eine C ++ - Klasse machen, die JNI-Aufrufe in Java kapselt, um eine Java-Sperre anzufordern; das würde den C ++ - Code vereinfachen, obwohl ich mich über den Overhead des Hin- und Hertransfers über JNI nur für Thread-Locks wundere.

Es scheint, dass dies nicht notwendig ist, nach dem Kommentar von @Radiodef. Es scheint, dass JNI MonitorEnter / MonitorExit -Funktionen enthält, die bereits das Sperren auf der C ++ - Seite behandeln. Es gibt Fallstricke, wenn Sie diese gleichzeitig mit herkömmlichen Sperren auf der Java-Seite verwenden, also lesen Sie bitte hier vor der Verwendung. Ich werde das ausprobieren, und ich erwarte, dass MonitorEnter / MonitorExit die Antwort sein wird und ich empfehle @Radiodef, eine Antwort aus dem Kommentar zu machen.

Schließen

Wie kann ich das richtig synchronisieren? Sollte pthread_mutex_ (un) lock arbeiten? Wenn nicht, was kann ich zum Synchronisieren zwischen dem C ++ - Thread und dem Java-Thread verwenden?

Hier wird kein JNI-spezifischer C ++ - Code bereitgestellt, da die JNI-Bridge funktioniert und ich Daten hin und her übertragen kann. Die Frage bezieht sich speziell auf die korrekte Synchronisation zwischen C ++ / Java-Threads, die ansonsten korrekt kommunizieren.

Wie bereits erwähnt, würde ich das Wahlverfahren lieber vermeiden, aber das könnte eine andere Frage sein. Der Legacy-C ++ - Code zeigt seinen Teil der Benutzeroberfläche in X / Motiv an, und wenn ich mich richtig erinnere, ist der C ++ - Thread oben der Ereignis-Thread für die Anzeige. Der Java-Thread wird der Java-Event-Dispatch-Thread sein, sobald die Java-Benutzerschnittstelle für diese Klasse eingesteckt ist, obwohl der Java-Thread vorerst ein automatisierter Test-Thread ist; So oder so, es ist ein separater Java-Thread.

Der C ++ - Thread ist an die JVM angehängt. Tatsächlich ist dies der C ++ - Thread, der die JVM erstellt hat, daher sollte sie standardmäßig angehängt sein.

Es ist mir gelungen, andere Java-Benutzeroberflächenelemente in dieses Programm zu integrieren, aber dies ist das erste Mal, dass C ++ nicht-atomare Daten von Java benötigt, die synchronisiert werden müssen. Gibt es einen allgemein akzeptierten richtigen Weg, dies zu tun?

    
Aaron 07.06.2017, 19:09
quelle

1 Antwort

1

Wenn beide Threads an die JVM angehängt sind, können Sie über die Funktionen JNIEnv und MonitorEnter(jobject) auf die Synchronisation des JNI zugreifen. So wie es klingt, erwirbt MonitorExit(jobject) eine Sperre für das bereitgestellte MonitorEnter , und jobject gibt die Sperre für das bereitgestellte MonitorExit frei.

HINWEIS: Es gibt einige Fallstricke, auf die Sie achten sollten! Beachten Sie den vorletzten Absatz von jobject 's Beschreibung und den letzten Absatz von MonitorEnter ' s Beschreibung über das Mischen und Vergleichen von MonitorExit / MonitorEnter mit anderen ähnlichen Mechanismen, die Sie sonst für kompatibel halten könnten.

>

Siehe hier

  

MonitorEnter

     

jint MonitorEnter (JNIEnv * env, Projektobjekt);

     

Gibt den Monitor an, der dem zugrunde liegenden Java-Objekt zugeordnet ist   nach obj. Gibt den Monitor ein, der dem Objekt zugeordnet ist, auf das verwiesen wird   von obj. Die Obj-Referenz darf nicht NULL sein. Jedes Java-Objekt hat a   Monitor damit verbunden. Wenn der aktuelle Thread bereits Eigentümer des   Monitor verbunden mit obj, erhöht es einen Zähler im Monitor   Gibt an, wie oft dieser Thread in den Monitor gelangt ist. Ob   Der mit obj verknüpfte Monitor gehört keinem Thread, der   Der aktuelle Thread wird zum Besitzer des Monitors und legt den Eintrag fest   Zähle diesen Monitor auf 1. Wenn ein anderer Thread bereits den Monitor besitzt   verknüpft mit obj wartet der aktuelle Thread bis der Monitor ist   veröffentlicht, versucht dann erneut, das Eigentum zu erwerben.

     

Ein über einen MonitorEnter JNI-Funktionsaufruf eingegebener Monitor kann nicht verwendet werden   mit der Java Virtual Machine-Anweisung monitorexit beendet oder a   synchronisierte Methodenrückgabe Ein MonitorEnter JNI-Funktionsaufruf und a   monitorenter Java virtual machine Anweisung kann Rennen in die   Monitor mit dem gleichen Objekt verbunden.

     

Um Deadlocks zu vermeiden, wurde ein Monitor über einen MonitorEnter JNI eingegeben   Der Funktionsaufruf muss mit dem MonitorExit JNI-Aufruf beendet werden, sofern nicht   Mit dem Aufruf DetachCurrentThread wird JNI implizit freigegeben   Monitore.

     

VERBINDUNG :

     

Index 217 in der JNIEnv-Schnittstellenfunktionstabelle.

     

PARAMETER :

     

env: der JNI-Schnittstellenzeiger.

     

obj: ein normales Java-Objekt oder Klassenobjekt.

     

RETURNS :

     

Gibt beim Erfolg "0" zurück; gibt bei einem Fehler einen negativen Wert zurück.

und

  

MonitorExit

     

jint MonitorExit (JNIEnv * env, Projektobjekt);

     

Der aktuelle Thread muss der Besitzer des zugeordneten Monitors sein   das zugrunde liegende Java-Objekt, auf das sich obj bezieht. Der Thread wird verringert   der Zähler zeigt an, wie oft er eingegeben wurde   Monitor. Wenn der Wert des Zählers Null wird, wird der aktuelle Thread angezeigt   gibt den Monitor frei.

     

Systemeigener Code darf MonitorExit nicht zum Beenden eines Monitors verwenden, durch den er eingegeben wurde   eine synchronisierte Methode oder eine monitorenter Java Virtual Machine   Anweisung.

     

VERKNÜPFUNG :

     

Index 218 in der JNIEnv-Schnittstellenfunktionstabelle.

     

PARAMETER :

     

env: der JNI-Schnittstellenzeiger.

     

obj: ein normales Java-Objekt oder Klassenobjekt.

     

RETURNS :

     

Gibt beim Erfolg "0" zurück; gibt bei einem Fehler einen negativen Wert zurück.

     

AUSNAHMEN :

     

IllegalMonitorStateException: Wenn der aktuelle Thread nicht die   überwachen.

Also sollte der C ++ - Code in der Frage, die versucht, pthreads zu verwenden, wie folgt geändert werden (der Code geht davon aus, dass der MonitorExit -Pointer irgendwie zuvor in typischer JNI-Art erfasst wurde):

%Vor%

Ein großes Lob an @Radiodef, der die Antwort gegeben hat. Leider war es als Kommentar. Ich wartete bis zum nächsten Tag, um Radiodef eine Antwort zu geben, also mache ich es jetzt. Danke, Radiodef, dass du mir den Anstupser gegeben hast, um das zu beheben.

    
Aaron 08.06.2017, 17:45
quelle