从线程返回值

时间:2017-07-07 14:25:07

标签: java multithreading javafx task

我在for循环中运行selenium测试需要时间。

我需要使用javafx progressbar来指明这些测试的进度。

所以我用for task替换了for循环中的代码。

以下是我的代码

runTests()方法返回String,显示为Alert

我无法从String内返回Task<Void>,因为返回类型为Void

测试代码位于runTest(data)方法内,返回truefalse

@FXML
private void handleRunTestButton(ActionEvent aEvent) throws IOException {
    String testResultMessage = runTests();
    if (!testResultMessage.equals("Testing Complete!")) {
        Alert alert = DialogUtils.getAlert("Info", "Information", testResultMessage, "info");
        alert.showAndWait();

    } else {
        Alert alert = DialogUtils.getAlert("Error", "Error(s)", testResultMessage, "error");
        alert.showAndWait();
    }

}

private String runTests() {
    /* xlsx read */
    FileInputStream fis = null;
    File testDataFile = null;
    try {
        fis = new FileInputStream(selectedTestDataFile);
    } catch (FileNotFoundException e) {
        return "File Input Stream Error: File Not Found";
    }

    // Finds the workbook instance for XLSX file
    XSSFWorkbook myWorkBook = null;
    try {
        myWorkBook = new XSSFWorkbook(fis);
    } catch (IOException e) {
        return "XSSFWorkbook I/O Error";
    }

    // Return first sheet from the XLSX workbook
    XSSFSheet mySheet = myWorkBook.getSheetAt(0);

    int totalWids = mySheet.getLastRowNum();

    final Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            for (int rowIndex = 1; rowIndex <= totalWids; rowIndex++) {
                updateProgress(rowIndex, totalWids);
                Row row = mySheet.getRow(rowIndex);
                if (row != null) {
                    String data = "";

                    Cell cellData = row.getCell(2);

                    if (cellData != null) {
                        data = cellWid.getStringCellValue();
                        boolean testresult = runTest(data);
                        System.out.println(rowIndex + ". data = " + data + ", testresult = " + testresult);
                    }
                }
            }
            return null;
        }
    };

    progressBar.progressProperty().bind(task.progressProperty());
    progressIndicator.progressProperty().bind(task.progressProperty());

    final Thread thread = new Thread(task, "task-thread");
    thread.setDaemon(true);
    thread.start();

    /* xlsx read */
    FileOutputStream fos = null;
    try {
        fos = new FileOutputStream(selectedTestDataFile);
    } catch (FileNotFoundException e) {
        try {
            myWorkBook.close();
        } catch (IOException e1) {
            return "Error: Please Close Workbook";
        }
        return "Error: File Not Found";
    }
    try {
        myWorkBook.write(fos);
    } catch (IOException e) {
        try {
            myWorkBook.close();
        } catch (IOException e1) {
            return "Error: Please Close Workbook";
        }
        return "Error: Workbook Write";
    }
    try {
        fos.close();
    } catch (IOException e) {
        try {
            myWorkBook.close();
        } catch (IOException e1) {
            return "Error: Please Close Workbook";
        }
        return "Error: File Output Stream";
    }
    try {
        myWorkBook.close();
    } catch (IOException e) {
        return "Error: Please Close Workbook";
    }
    try {
        fis.close();
    } catch (IOException e) {

        return "Error: Input file format";
    }
    return "Testing Complete!";
}

但是,现在它在测试仍在运行时返回Testing Complete!

我是multithreading的新手。请建议我如何构建代码。

如何让runTests()方法从

中返回String
final Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            for () {

            }
            return null;
        }
    };

在此之前,当我没有使用Task时,我的代码正确地显示了警报,但是尽管在for循环中设置了进度,但进度条没有更新。

2 个答案:

答案 0 :(得分:1)

一般来说,您的代码看起来非常可靠,但有几个问题。 您创建的任务可以解决问题,进度条也可以工作,但它使用一个线程,因此返回测试完成而不确认线程的进度是错误的。因为测试在一个线程中,并且该方法返回一个值而不依赖于它,所以在测试完成之前返回该值。 调用thread.start()时,线程会从当前线程中单独开始执行,这意味着即使线程没有完成,代码也会继续执行。

您有两种可能的选择:保留线程,或者不保留。如果不保留线程,则意味着测试在方法中执行,该方法导致调用它的javaFX事件等待测试完成。这是一个坏主意,因为现在javaFX线程被卡住了,窗口无法处理任何其他事件(基本上是iresponsive)。

一个好的选择是保留线程,只有在线程结束时才能显示一个对话框,指示测试是否完整。为此,您可以使用Platform.runLater(runnable)并向其传递一个显示对话框的Runnable对象:

Platform.runLater(()->{
   //show dialog
});

这是必需的,因为你不能在javaFX线程中显示对话框。这允许你在javaFX线程中运行一些东西。

另一个问题是您正在访问线程之外的文件。这意味着在线程运行测试的同时,您尝试访问文件并写入它们。您应该在线程中或在启动之前写入文件,而不是这样做。

为了总结这一切,你应该使用你的线程来执行测试并显示指示测试是否完成的对话框。在线程仍在执行测试时,不应该写入测试文件,而是在线程完成后写入,所以你可以在任务结束时完成。

public void runTests(){
    if(testsRunning) return;

    testsRunning = true;

    final Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            FileInputStream fis = null;
            File testDataFile = null;
            try {
                fis = new FileInputStream(selectedTestDataFile);
            } catch (FileNotFoundException e) {
                displayResponse("File Input Stream Error: File Not Found");
            }

            // Finds the workbook instance for XLSX file
            XSSFWorkbook myWorkBook = null;
            try {
                myWorkBook = new XSSFWorkbook(fis);
            } catch (IOException e) {
                displayResponse("XSSFWorkbook I/O Error");
            }

            // displayResponse(first sheet from the XLSX workbook
            XSSFSheet mySheet = myWorkBook.getSheetAt(0);

            int totalWids = mySheet.getLastRowNum();

            for (int rowIndex = 1; rowIndex <= totalWids; rowIndex++) {
                updateProgress(rowIndex, totalWids);
                Row row = mySheet.getRow(rowIndex);
                if (row != null) {
                    String data = "");

                    Cell cellData = row.getCell(2);

                    if (cellData != null) {
                        data = cellWid.getStringCellValue();
                        boolean testresult = runTest(data);
                        System.out.println(rowIndex + ". data = " + data + ", testresult = " + testresult);
                    }
                }
            }

            /* xlsx read */
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(selectedTestDataFile);
            } catch (FileNotFoundException e) {
                try {
                    myWorkBook.close();
                } catch (IOException e1) {
                    displayResponse("Error: Please Close Workbook");
                }
                displayResponse("Error: File Not Found");
            }
            try {
                myWorkBook.write(fos);
            } catch (IOException e) {
                try {
                    myWorkBook.close();
                } catch (IOException e1) {
                    displayResponse("Error: Please Close Workbook");
                }
                displayResponse("Error: Workbook Write");
            }
            try {
                fos.close();
            } catch (IOException e) {
                try {
                    myWorkBook.close();
                } catch (IOException e1) {
                    displayResponse("Error: Please Close Workbook");
                }
                displayResponse("Error: File Output Stream");
            }
            try {
                myWorkBook.close();
            } catch (IOException e) {
                displayResponse("Error: Please Close Workbook");
            }
            try {
                fis.close();
            } catch (IOException e) {
                displayResponse("Error: Input file format");
            }

            displayResponse("Testing Complete!");

            return null;
        }
        private void displayResponse(String testResultMessage){
            Platform.runLater(()->{
                if (testResultMessage.equals("Testing Complete!")) {
                    Alert alert = DialogUtils.getAlert("Info", "Information", testResultMessage, "info");
                    alert.showAndWait();

                } else {
                    Alert alert = DialogUtils.getAlert("Error", "Error(s)", testResultMessage, "error");
                    alert.showAndWait();
                }
                testsRunning = false;
            });
        }
    };

    progressBar.progressProperty().bind(task.progressProperty());
    progressIndicator.progressProperty().bind(task.progressProperty());

    final Thread thread = new Thread(task, "task-thread");
    thread.setDaemon(true);
    thread.start();
}

因此,此代码现在可以在线程中执行所有相关测试,并且不会因处理事件而中断窗口。这有一个问题:有人可能会在测试运行时再次按下runTests按钮。一种选择是使用一个布尔值来指示测试是否已经处于活动状态,并在调用runTests时检查其值,我将其添加并称为testsRunning。当测试完成(已完成或未完成)并显示响应对话框时,将调用displayResponse

希望我能帮忙,并为长时间的回答感到抱歉。

答案 1 :(得分:0)

首先,你不能在没有阻塞的情况下从线程中返回一个值。相反,尝试在线程完成时调用方法。

假设我们有这个线程运行一些密集型任务(在这种情况下是一个简单的forloop),你希望它在完成时“返回”总和:

private void startMyThread() {
    Thread t = new Thread( () -> {
        System.out.println("Thread Started.");

        // Some intensive task
        int sum = 0;
        for(int i = 0; i < 1000000; i++) {
            sum++;
        }

        System.out.println("Thread ending.");
        threadIsDone(sum);
    });

    System.out.println("Starting Thread.");
    t.start();
}

而不是从startMyThread()获取返回值,而是等到执行您的操作,直到调用threadIsDone()

private void threadIsDone(int sum) {
    Platform.runLater( () -> {
        /* Update Progress Bar */
        System.out.println("Updated Progress Bar");
    });

    System.out.println("Thread ended.");
}

你会注意到我在方法中使用Platform.runLater(),因为JavaFx元素的所有更新都需要在主线程上完成,因为我们从另一个线程调用threadIsDone(),我们需要告诉JavaFx我们希望它在主线程上执行此操作,当前线程。