2009-07-21 3 views
4

스레드에서 JNI를 사용하여 네이티브 함수를 호출하는 데 어려움을 겪고 있습니다.시간이 많이 걸리는 JNI 작업을 스레드로 호출

네이티브 함수는 계산 집약적 인 작업을 수행하는 레거시 코드입니다. 나머지 프로그램은 동결하지 않으므로 백그라운드 스레드에서 계산을 수행해야합니다. EventBus은 계산 결과를 주 프로그램으로 다시 보내는 데 사용됩니다. 기본적으로

매우 간단해야한다, 이런 식으로 뭔가 : 이제

public class CalculationEngine { 
    private CalculationEngine(){} 

    public static void calculateInBackground(final Parameters parameters) { 

    new Thread(new Runnable() { 
     public void run() { 
     // Someone might change the parameters while our thread is running, so: 
     final Parameters clonedParameters = parameters.clone(); 
     Results results = new Results(); 
     natCalc(clonedParameters, results); 
     EventBus.publish("Results", results); 
     } 
    }).start(); 

    } 

    public static void calculateNormally(final Parameters parameters) { 
    Results results = new Results(); 
    natCalc(parameters, results); 
    EventBus.publish("Results", results); 
    } 

    private static native synchronized void 
    natCalc(Parameters parameters, Results results);  
} 

, 메인 프로그램이 잘 작동 차단 calculateNormally 방법,하지만 단지 백그라운드 스레드를 구성한 calculateInBackground 방법, 똑같은 일을하면 연속적으로 호출 될 때 네이티브 코드 에서 다양한 크래시가 발생합니다.. 연속적으로 이전 스레드가 끝나고 결과를 반환 한 후에 만 ​​다시 호출된다는 것을 의미합니다. 한 번에 하나의 인스턴스 만 실행될 수 있도록하기 위해 기본 코드는 synchronized으로 표시됩니다.

제 질문은 기본 코드가 다른 스레드에서 호출되는지 여부에 따라 네이티브 코드가 어떻게 다르게 동작 할 수 있습니까? 네이티브 코드가 "상태"를 유지하는 것과 같고, 메인 스레드가 아닌 다른 스레드 내에서 호출 될 때 종료하지 않습니다. 완료 후 스레드를 "정리"또는 "플러시"할 수있는 방법이 있습니까? JNI에 뭔가가 있어야합니다. & 간단히 알 수없는 스레드.

어떤 힌트를 주셔서 감사합니다!

+0

답변으로 추가 하시겠습니까? _ 실제 해결책이므로 매우 유용합니다. –

+0

좋은 아이디어. 이제 대답입니다. –

답변

7

검색 솔루션을 찾은 후 "I've found JNI to be very buggy when called from seperate threads... So make sure only one thread ever calls your native code!"이라는 문구를 찾았습니다. 그것은 사실 인 것처럼 보인다. 해결 방법은 영구적 인 "재사용 가능한"스레드를 유지하는 것입니다. - 나는 Executors.newSingleThreadExecutor()을 사용했으며 해당 스레드에서만 원시 코드를 호출했습니다. 그것은 작동합니다.

JNI 관점과의 차이점은 주 스레드와 다른 스레드 사이의 차이점이 아니라 연속 호출에서 다른 스레드 사용에 있습니다. 문제가있는 코드에서는 매번 새로운 스레드가 생성되었습니다. 그렇게하면 효과가 있지만 그렇지 않습니다. (그리고 아니, 나는 JNIEnv 포인터를 캐싱하지 않는다.)

JNI 버그 이건, 원시 코드의 버그 이건, OS와 상호 작용이있는 것이 든, 재미있는 것인가. 그러나 때로는 10000 개 이상의 기존 코드를 자세히 디버그 할 수있는 기회가없는 경우도 있습니다. 다음 예제 코드의 버전을 일하고, 이제 이것을 해결을 부르 자 : JNI 사용에

public class CalculationEngine { 
    private CalculationEngine(){} 

    private static Parameters parameters; 
    private static ExecutorService executor = Executors.newSingleThreadExecutor(); 

    private static Runnable analysis = new Runnable() { 
     public synchronized void run() { 
     Results results = new Results(); 
     natCalc(parameters, results); 
     EventBus.publish("Results", results); 
     } 
    }; 

    public static synchronized void 
    calculateInBackground(final Parameters parameters) { 
     CalculationEngine.parameters = parameters.clone(); 
     executor.submit(analysis); 
    } 

    private static native synchronized void 
    natCalc(Parameters parameters, Results results);  
} 
+1

상황에 맞는 특정 버그를 발견하거나 낮은 수준에서 어떤 일이 일어나는지 식별 할 수없는 경우 웹에있는 임의의 사람의 주장에 관계없이 JNI를 비난하면 안됩니다. 더 자세한 내용은 http://bugs.sun.com/bugdatabase/ – kdgregory

+0

을 참조하십시오. * 귀하의 * JNI 코드가 호출간에 데이터를 보유하고있을 가능성이 높습니다. 또는 여러 개의 동시 스레드가 변경 될 수있는 방식으로 데이터를 노출 할 가능성이 있습니다 (단일 스레드에서이 방향으로 실행될 때 작동 함). – kdgregory

+0

버그를 제출하려고 합니다만, 작업하고있는 원시 코드가 독점적이어서 썬으로 보낼 수 없으며 코드없이 어떻게 재현 할 수 있습니까? 어쨌든, 이제 나는 웹상의 임의의 사람들 중 하나인데, JNI 호출이 단일 스레드에서 수행 될 때 더 잘 작동한다고 주장하는 사람 :-) –

3

내 조언은 당신이 가능하게 그것을 피할 수없는 경우입니다. 귀하의 안정성 문제가 발생할 가능성이 있습니다. 가능한 대안은 다음과 같습니다.

  1. Java의 원시 라이브러리를 다시 코드화하십시오.
  2. C/C++/whatever에서 네이티브 라이브러리에 대한 래퍼 명령을 작성하고 java.lang.Process와 친구를 사용하여 실행하십시오.
  3. 네이티브 라이브러리를 데몬으로 전환하고 소켓을 사용하여 액세스하십시오.
+3

의견을 보내 주셔서 감사합니다. 대안 1. 상호 작용할 기존의 중요한 코드가 많을 때 실용적이지 않습니다. 대안 2와 3은 확실히 실행 가능합니다. 그러나 네이티브와 자바 측 사이에 물건을 보내려면 일종의 프로토콜을 만들어야합니다. JNI는 원칙적으로 몇 가지 함수 호출 만 있으면 가장 간단합니다. 그러나 실제로 그것은 그것의 자신의 특성이있는 것처럼 보인다; JNI 계층을 아무리 조심스럽게 만들었더라도 기존 기본 코드의 결함을 노출시킬 수 있습니다. –

+0

(개인) 프로토콜을 정의해야 할 필요가 있습니다.대안 2와 3의 또 다른 문제점은 상호 작용의 오버 헤드가 더 크다는 것입니다. fork/exec 또는 RPC 대 프로 시저 호출. 그러나 그럼에도 불구하고, 쇼 스토퍼 이유가 없다면 JNI가 아닌 솔루션을 계속 사용할 수 있습니다. –

+1

JNI는 희미한 마음이 아닙니다. 네이티브/CLR 상호 작용에 대한 Visual Studio 접근 방식은 훨씬 쉽습니다. 그러나 단순성면에서 부족한 점은 융통성이 있습니다. JNI/JVM 모델에 결함이있는 경우 지금은 사라 졌을 것이지만 실제로는 CLR보다 단순한 모델입니다. JNI를 피하는 것이 좋습니다. JNI를 피하는 방법은 스레드가 작동하는 방식과 JVM의 기본 데이터 구조가하는 일을 제한적으로 이해하는 사람에게 좋습니다. 하지만 comp sci 지식 수준이 높은 사람은 JNI를 포기하기 전에 주제에 대해 공부하는 것이 좋습니다. –

1

답변을 얻는 동안 가능한 근본 원인에 대해서는 너무 많이 생각하지 않았습니다. 여기에 몇 가지 가능성이 있지만 다른 것들도 있습니다.Windows에 적용됩니다.

아파트 스레드 COM 개체가 있습니다. VB에서 만들 수있는 유일한 유형 인 아파트 스레드 COM 개체는 해당 스레드를 만드는 스레드에서만 사용할 수 있습니다.

가장 같은 보안 기능은 자주 분리됩니다. 초기화 코드가 스레드의 컨텍스트를 수정 한 경우 컨텍스트가 올바르다 고 예상되는 이후의 호출은 실패합니다.

스레드 별 메모리 저장소는 일부 응용 프로그램에서 다중 스레드 (Java에도 이러한 기능이 있음)를 지원하는 기술입니다.

+0

감사합니다. 실제로 Windows .dll이고 COM 개체 관련 설명은 많은 의미가 있습니다. 나는 그것을 몰랐다. –

관련 문제