将耗时的JNI任务作为线程调用

Joo*_*kka 4 java-native-interface multithreading

我在使用线程中的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);      
}
Run Code Online (Sandbox Code Playgroud)

现在,calculateNormally阻止主程序的calculateInBackground方法工作正常,但是只构造后台线程来执行相同操作的方法在本机代码连续调用时会导致各种崩溃.连续地,我的意思是它只在前一个线程完成后再次调用并返回结果.请注意,本机代码已标记synchronized为确保一次只能运行一个实例.

我的问题是,本机代码如何根据它是从主线程调用还是从其他线程调用来表现不同?这就像本机代码保持"状态",而不是真正退出,当它从主线程以外的线程中调用时.有没有办法在线程完成后"清理"或"刷新"?JNI和Threads中必定有一些我根本不知道的东西.

谢谢你的任何提示!

Joo*_*kka 7

我找到了一个有效的解决方案,经过谷歌搜索并找到"我发现JNI从单独的线程调用时非常错误......"所以确保只有一个线程可以调用你的本机代码!.这似乎是真的; 解决方案是保持一个持久的,"可重用的"线程 - 我使用Executors.newSingleThreadExecutor()- 并仅从该线程调用本机代码.有用.

因此,与JNI的观点不同之处在于主线程与其他线程之间的差异,而是在连续调用中使用不同的线程.请注意,在有问题的代码中,每次都构造一个新线程.它应该以这种方式工作,但事实并非如此.(不,我不是在缓存JNIEnv指针.)

无论是JNI错误,本机代码中的错误,它们与操作系统之间的交互还是其他什么,都会很有趣.但有时候你根本没有机会详细调试10000多行现有代码,但是,你很高兴能让它运行起来.这是示例代码的工作版本,我们称之为解决方法:

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);      
}
Run Code Online (Sandbox Code Playgroud)