为啥我不能同时从 Java 执行不同的 Matlab 函数?

Posted

技术标签:

【中文标题】为啥我不能同时从 Java 执行不同的 Matlab 函数?【英文标题】:Why can't I execute different Matlab functions from Java concurrently?为什么我不能同时从 Java 执行不同的 Matlab 函数? 【发布时间】:2016-07-19 20:04:23 【问题描述】:

我有两个 Java Servlet:DataFetcherServletUploaderServlet。两个 servlet 调用 2 个不同的 Java 方法,这些方法依次通过 JNI 调用它们对应的 Matlab 函数,每个函数都被编译成一个单独的 Java jar 文件作为库使用。该应用程序由 AJAX 提供支持,以创建类似桌面的感觉。对于UploaderServlet,用户可以上传一个excel文件到这个servlet,解析后的数据然后被传递给一个Java方法,然后调用编译好的Matlab函数来生成并保存很多图像(目前超过5000张图像),因为这个需要很多时间,我使用ExecutorService 在后台执行它。但是发送DataFetcherServlet 的新请求也会调用另一个已编译的Matlab 函数,直到图像生成部分完成为止。我不知道为什么它会阻止新请求,即使请求被发送到不同的 servlet。

DataFetcherServlet.java

public class DataFetcherServlet extends HttpServlet 

    @Inject
    private CdfReader reader; // An EJB to get a data array from Matlab

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException 
        try 
            String filePath = "path/to/file";
            Object[] result = reader.read(filePath); // reader.read() is just a wrapper around the method in the jar file mentioned above that actually calls the matlab function to return an array of number
            MWNumericArray array = (MWNumericArray)result[0] // This will block while the other Matlab function is generating the images.
            .
            .
            .
         catch (MWException ex) 
            Logger.getLogger(DataFetcherServlet.class.getName()).log(Level.SEVERE, null, ex);
    

UploaderServlet.java

public class UploaderServlet extends HttpServlet 
    @Inject
    private ExcelIonImageGenerator generator; // An EJB to call Matlab to generate the images

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException 
        try 
            String dir = "path/to/parent/directory";
            Path excel = Paths.get(dir+ "excel", part.getSubmittedFileName()); // Path to where the uploaded excel file is stored
            if (!Files.exists(excel))
                Files.copy(part.getInputStream(), excel);
            // ExcelExtractor is a helper class to parse the excel file.
            Double[][] ranges = ExcelExtractor.extractSheet(WorkbookFactory.create(excel.toFile()));
            // This will call a Java library method which in turns call the Matlab function
            // to generate the images (over 5000 in this case)
            // See the code for this method below.
            generator.generate(dir+ "images" + File.separator, ranges);
         catch (MWException | InvalidFormatException ex) 
            Logger.getLogger(UploaderServlet.class.getName()).log(Level.SEVERE, null, ex);
        
    

ExcelIonImageGenerator.java

import com.mathworks.toolbox.javabuilder.*; // Matlab SDK needed to integrate with Java
import java.util.concurrent.*;
import java.util.logging.*;
import javax.annotation.PreDestroy;
import javax.ejb.Stateless;
import save_ion_image_for_all_ranges_in_spreadsheet.Class1; // The jar file which contains code to call Matlab code through JNI

@Stateless
public class ExcelIonImageGenerator 
    private final Class1 clazz1;
    private ExecutorService pool;

    public ExcelIonImageGenerator() throws MWException 
        clazz1 = new Class1();
        pool = Executors.newFixedThreadPool(1);
    

    public void generate(String path, Double[][] ranges) throws MWException 
        // Submit this task to the ExecutorService so it can be processed
        // in a different thread than the caller thread
        pool.submit(() -> generateHelper(path, ranges, clazz1), 1);
    

    private void generateHelper(String path, Double[][] ranges, Class1 clazz) 
        try 
            // This method was generated by Matlab tool, it calls the native
            // Matlab code through JNI, and it will block any request that will call
            // other Matlab functions until it finishes.
            clazz.save_ion_image_for_all_ranges_in_spreadsheet(path, ranges);
         catch (MWException ex) 
            Logger.getLogger(ExcelIonImageGenerator.class.getName()).log(Level.SEVERE, null, ex);
        
    

【问题讨论】:

因为主要的 Matlab 执行发生在单个线程上? 可能。我们在服务器上安装了 matlab 运行时。但是在为某些用户执行某些 matlab 函数时,每个用户都会被阻止 是的,它会阻止其他用户。您要么必须启动多个 Matlab 实例(例如,每个用户一个)并适当地路由命令,要么使用类似 Matlab Production Server 你知道我们如何启动多个 Matlab 实例吗? 如何启动第一个?您必须以某种方式启动并连接到它。重复多次? 【参考方案1】:

您有三个选择:

    启动调用 Matlab 的 Java 应用程序的多个进程。来自单个进程的调用使用具有进程范围锁的相同 MCR,但是来自不同进程的调用将在不同的 MCR 计算引擎上运行。 使用Matlab Production Server,基本方便多个MCR的使用。这是一个需要单独许可和安装的工具包。 除非您有非常具体的性能问题,否则您不必将自己限制在运行 MCR/编译代码。您实际上可以在服务器上安装 Matlab 本身,从同一个 Java 进程启动多个实例(无头等),并与它们进行通信,例如通过MatlabControl或新官方MATLAB Engine API for Java。

MatlabCentral 上有一个很好的answer from MathWorks Support Team,详细解释了 MCR 的这些限制。

【讨论】:

非常感谢您。第一个选项是不可能的,因为我们正在运行一个 Web 应用程序,所以为小用户创建这么多进程会杀死应用程序,因为进程内存很重,我们不知道有多少用户将在同时。第二种选择,我们可以研究一下。选项 3 是目前​​最可行的选项。衷心感谢您的帮助

以上是关于为啥我不能同时从 Java 执行不同的 Matlab 函数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥多处理不能并行工作? [复制]

为啥我不能从不同的 ViewController 视图设置视图?

为啥我不能在一个文件中同时定义 Test 和 test 类?

Java 泛型与 C++ 模板有何不同?为啥我不能使用 int 作为参数?

为啥我不能从不同命名空间中的朋友类更改类的私有成员?

为啥我不能从我的 python 脚本创建可执行文件?