从线程返回值 [英] Returning values from thread

查看:300
本文介绍了从线程返回值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am running selenium tests inside a for loop which takes time.

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

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

So I replaced the code inside for loop with a task.

以下是我的代码

runTests()方法返回a 字符串显示为警告

The runTests() method returns a String which is displayed as an Alert

I无法从任务< Void> 中返回字符串,因为返回类型为 Void

I cannot return a String from inside Task<Void> as the return type is Void

测试代码在 runTest(data)方法内,返回 true false

The test code is inside runTest(data) method which returns true or false

@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!";
}

然而,现在它返回测试完成!当测试仍在运行时。

However, now it returns Testing Complete! while the tests are still running.

我是多线程的新手。请建议我如何构造代码。

I am new to multithreading. Please suggest me how to structure the code.

如何使 runTests()方法返回字符串来自内部的价值

How can I make the runTests() method return a String value from inside

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

            }
            return null;
        }
    };

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

Before this, when I didn't use Task my code showed the alert properly however the progress bar did not update despite of setting the progress from within the for loop.

推荐答案

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

Your code, in general, seems pretty solid, but there are several problems. The task you created does the trick, and the progress bar will work, but it uses a thread so returning that the tests are complete without confirming the progress of the thread is wrong. Because the tests are in a thread and the method returns a value without being dependent on it, the value is returned before the tests are done. When calling thread.start() the thread starts execution seperatly from your current thread, meaning that your code continues to execute as usual even if the thread was not done.

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

You have 2 possible options: keep the thread, or don't. If you don't keep the thread, that means that the tests are executed in the method which causes the javaFX event that called it to wait for the tests to finish. This is a bad idea because now the javaFX thread is stuck and the window can't handle any other events (basically, iresponsive).

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

A good option is to keep the thread, only that at the end of the thread you could show a dialog indicating whether the tests were complete or not. To do that you can use Platform.runLater(runnable) and pass it a Runnable object which shows the dialog:

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

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

It is required because you can't show a dialog while not in the javaFX thread. This allows you to run something in the javaFX thread.

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

Another issue is the fact that you're accessing the files outside of your thread. Meaning that at the same time the thread runs your test, you attempt to access the files and write to them. Instead of doing that, you should either write to the file in the thread or before it is started.

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

To summerize it all, you should use your thread to execute the tests and show the dialogs which indicate whether or not the tests were completed. Writing to your test file should not be done while the thread is still executing tests, but rather after the thread was finished, so you can do it at the end of the task.

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

So this code now does everything test related in the thread and doesn't interrupt your window from handling events. There is one problem from this: someone might press the runTests button again, while the tests are running. One option is to use a boolean indicating whether the tests are already active and check its value when runTests is called which I added and is called testsRunning. displayResponse is called when the tests where finished (completed or not) and it displayes the response dialog.

希望我帮忙,对不起答案很长。

Hope I helped, and sorry for the long answer.

这篇关于从线程返回值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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