从线程返回值 [英] Returning values from thread
问题描述
我在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屋!