3

ゲーム アプリに (メイン スレッド以外に) 2 つのスレッドがあるとします。

  • GLRenderer スレッド (Android が提供GLSurfaceView.Renderer)
  • 別スレ(ゲームスレ)

どちらのスレッドも JNI を使用して、アプリの特定の C++ (つまり Android NDK) コンポーネントを呼び出します。

Javaで直接IntBuffer割り当てられていると仮定します(たとえば、GLRendererスレッドからですが、これを想定しないでください)。事実:

  • このダイレクト バッファは、GLRenderer スレッドからのネイティブ コードによって (つまり、JNI 経由で呼び出される C++ コンポーネントによって)読み取られます。
  • このダイレクト バッファは、別のスレッド (ゲーム スレッド) から書き込まれることがあります。

次の 2 つのシナリオで、同期 (実際にはデータの可視性を確保する) の (最善の) 方法は何IntBufferですか?

  • シナリオ #1: ゲーム スレッドの Java コードがIntBuffer(たとえば 経由でIntBuffer.put())に書き込みます。
  • シナリオ #2: ゲーム スレッドから呼び出されたネイティブ コードがIntBuffer

標準の Java 同期は両方のケースで機能すると考えていました。

public void onDrawFrame(GL10 gl) { // the GLRenderer thread
    // ...
    synchronized (obj) {
        callNativeCode1(); // a JNI call; this is where the C++ native code reads the IntBuffer
    }

}

public void run() { // the game thread
    // ...

    synchronized (obj) {
        intBuffer.put(...); // writing the buffer from managed code
    }

    // ...
    synchronized (obj) {
        callNativeCode2(); // a JNI call; writing the buffer from C++ native code
    }
}
4

3 に答える 3

2

JNI とのメモリ共有の詳細についての知識はありませんが、AtomicIntegerArray.

オプションは次のとおりです。

  1. synchronized- JNI で何らかの形で同じことを達成する必要があります - 簡単ではありません。
  2. volatile-配列全体を作成するvolatileと問題が発生します。
  3. atomics - 最適なルートだと思います。

以下については、パッケージ java.util.concurrent.atomicを参照してください。

AtomicIntegerArray、AtomicLongArray、および AtomicReferenceArray クラスは、アトミック操作のサポートをこれらの型の配列にさらに拡張します。これらのクラスは、通常の配列ではサポートされていない、配列要素の揮発性アクセス セマンティクスを提供することでも注目に値します。

これにより、JNI コードが Java のキャッシュ フラッシュ セマンティクスをバイパスするために何もしない限り、JNI パッケージはデータの一貫した更新されたビューを参照する必要があります。

これを確認するためにいくつかの重要な調査をお勧めしますが、これがあなたが探しているものを達成する唯一の方法だと思います.

于 2013-10-30T12:08:40.870 に答える
1

同期に相当する JNI を使用して、2 つのスレッドによってアクセスされるネイティブ データが同期されることを保証できます。

//before read/writing shared data
(*env)->MonitorEnter(obj);

...                      /* synchronized block */

//after read/writing shared data
(*env)->MonitorExit(obj);

詳細については、このIBM の記事を参照してください。

編集: もう少し掘り下げた後、C コードの同期を維持する方法は VM 実装に依存することがわかりました (セクション 2.1: http://www.hdfgroup.org/hdf-java-html/JNI/を参照)。恐ろしい部分はこれです:

...そしてほとんどの場合、C コードが何をしなければならないかを知る方法はありません。

残念ながら、Android がこれを具体的にどのように処理するか、または Android バージョン間で一貫して処理されるかどうかについての情報は見つかりません。

ただし、別の興味深い情報が Android 開発者サイト ( http://developer.android.com/training/articles/smp.html ) によって提供されており、ARM CPU が提供するメモリの一貫性が弱いことを示しているため、これが C コードのデフォルトの動作である可能性があります。 .

基本的に問題は次のようになります:スレッド アクセスが Java で同期されるときに c データが同期されるかどうか。リンク 1 ( http://www.hdfgroup.org/hdf-java-html/JNI/ ) ではこれに関する直接的な答えが得られないため、リンク 2 ( http://developer.android.com/training/記事/smp.html ) は、ARM CPU が提供するメモリの一貫性が弱いことを示しており、C コードでモニター呼び出しを再度実行する方が安全な方法のようです。

于 2013-11-01T13:41:50.613 に答える
1

私があなたの質問を正しく理解している場合、つまり、callNativeCode1()(読み取り) とcallNativeCode2()(書き込み) が同時に発生しないようにする必要があり、両方のメソッドがネイティブである場合、それらを同期しても JNI レイヤーでは効果がありません。

オプションは、読み取り/書き込み操作の両方で呼び出される Java メソッドを作成し、それを同期することです。

public void synchronized readOrWrite(boolean read){
    if (read){
        callNativeCode1();
    }
    else {
        callNativeCode2();
    }
}

したがって、コードは次のようになります。

    public void onDrawFrame(GL10 gl) { // the GLRenderer thread
        readOrWrite(true);
    }

    public void run() { // the game thread

    synchronized (obj) {
        intBuffer.put(...); // writing the buffer from managed code
    }

    readOrWrite(false);
于 2013-11-01T14:02:26.030 に答える