将耗时的JNI任务作为线程调用 [英] Invoking time-consuming JNI task as a thread

查看:300
本文介绍了将耗时的JNI任务作为线程调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用线程中的JNI调用本机函数时,我遇到了一个棘手的问题.

I'm having a tough problem with invoking a native function using JNI from a thread.

本机函数是执行计算密集型任务的遗留代码.由于我不想冻结程序的其余部分,因此应在后台线程中执行计算. EventBus 用于将计算结果发送回主程序.

The native function is legacy code that performs a computation-intensive task. Since I'd like not to freeze the rest of the program, the computation should be performed in a background thread. EventBus is used to send the calculation result back to the main program.

基本上应该非常简单,就像这样:

Basically it should be quite simple, something like this:

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,以确保一次只能运行其一个实例.

Now, the calculateNormally method, which blocks the main program, works fine, but the calculateInBackground method, which just constructs a background thread to do the same thing, causes various crashes in the native code when it's invoked consecutively. By consecutively I mean that it's called again only after the previous thread has finished and returned the result. Note that the native code is marked synchronized to ensure that only one instance of it can be running at a time.

我的问题是,根据本地代码是从主线程还是从其他线程调用,本机代码在行为上有何不同?从本地线程之外的其他线程中调用本地代码时,就像本机代码保持状态",而不是真正退出.有没有一种方法可以在完成线程后清理"或刷新"线程? JNI&中必须有某些内容我根本不知道的线程.

My question is, how on earth can the native code behave differently depending on whether it's invoked from the main thread, or from some other thread? It's like the native code were keeping "state", and not really quitting, when it's called from within a thread other than the main thread. Is there a way to "clean" or "flush" a thread after it's finished? There must be something in JNI & Threads that I simply don't know.

感谢任何提示!

推荐答案

在谷歌搜索并找到短语.这似乎是真的.解决方案是保持一个持久的可重用"线程(我使用了Executors.newSingleThreadExecutor()),并且仅从该线程调用本机代码.可以.

I figured out a working solution, after googling and finding the phrase "I've found JNI to be very buggy when called from seperate threads... So make sure only one thread ever calls your native code!". It seems to be true; the solution is to keep a persistent, "reusable" thread around - I used Executors.newSingleThreadExecutor() - and to call the native code only from that thread. It works.

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

So the difference from JNI point of view was not between main thread vs. some other thread, but in using different threads in consecutive calls. Note that in the problematic code a new thread was constructed each time. It should work that way, but it doesn't. (And no, I'm not caching JNIEnv pointer.)

这是否是JNI错误,本机代码中的错误,它们与OS之间的交互作用或其他原因,都将很有趣.但是有时候您只是没有机会详细调试10000多行现有代码,但是,您很高兴将其投入使用.这是示例代码的工作版本,我们称其为解决方法:

Whether it's a JNI bug, bug in the native code, something in the interaction between them and OS or whatever, would be interesting to know. But sometimes you just have no chance to debug 10000+ lines of existing code in detail, however, you're happy to get it to work. Here's working version of the example code, let's call this a workaround:

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);      
}

这篇关于将耗时的JNI任务作为线程调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆