【问题标题】:Java Process.waitFor() and Readline hangsJava Process.waitFor() 和 Readline 挂起
【发布时间】:2012-10-10 04:42:28
【问题描述】:

首先,这是我的代码:

import java.io.*;
import java.util.Date;

import com.banctecmtl.ca.vlp.shared.exceptions.*;

public class PowershellTest implements Runnable {

public static final String PATH_TO_SCRIPT = "C:\\Scripts\\ScriptTest.ps1";
public static final String SERVER_IP = "XX.XX.XX.XXX";
public static final String MACHINE_TO_MOD = "MachineTest";

/**
 * @param args
 * @throws OperationException 
 */
public static void main(String[] args) throws OperationException {

    new PowershellTest().run();

}

public PowershellTest(){}

@Override
public synchronized void run() {
    String input = "";
    String error = "";

    boolean isHanging = false;

    try {

        Runtime runtime = Runtime.getRuntime();
        Process proc = runtime.exec("powershell -file " + PATH_TO_SCRIPT +" "+ SERVER_IP +" "+ MACHINE_TO_MOD);
        proc.getOutputStream().close();
        InputStream inputstream = proc.getInputStream();
        InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
        BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

        proc.waitFor();

        String line;
        while (!isHanging && (line = bufferedreader.readLine()) != null) {
            input += (line + "\n");
            Date date = new Date();
            while(!bufferedreader.ready()){

                this.wait(1000);
                //if its been more then 1 minute since a line has been read, its hanging. 
                if(new Date().getTime() - date.getTime() >= 60000){

                    isHanging = true;
                    break;

                }
            }
        }

        inputstream.close();
        inputstream = proc.getErrorStream();
        inputstreamreader = new InputStreamReader(inputstream);
        bufferedreader = new BufferedReader(inputstreamreader);
        isHanging = false;

        while (!isHanging && (line = bufferedreader.readLine()) != null) {
            error += (line + "\n");
            Date date = new Date();
            while(!bufferedreader.ready()){

                this.wait(1000);
                //if its been more then 1 minute since a line has been read, its hanging. 
                if(new Date().getTime() - date.getTime() >= 60000){

                    isHanging = true;
                    break;

                }
            }
        }
        inputstream.close();

        proc.destroy();

    } catch (IOException e) {
        //throw new OperationException("File IO problem.", e);
    } catch (InterruptedException e) {
        //throw new OperationException("Script thread problem.",e);
    }
    System.out.println("Error : " + error + "\nInput : " + input);
}

}

我目前正在尝试运行将在远程服务器上启动/停止 vm (VMWARE) 的 powershell 脚本。 脚本从命令行工作,这段代码也是如此。问题是,我讨厌我必须为这样的工作使用线程(并让它等待脚本响应,如进一步解释的那样)。我不得不这样做,因为 BufferedReader.readline() 和 proc.waitFor() 都永远挂起。

脚本从 cmd 运行时执行时间较长。从验证服务器身份验证和执行实际脚本开始,它会停滞 30 秒到 1 分钟。根据我从调试中看到的情况,当它开始从脚本接收这些延迟时,readline 挂起。

我也很确定这不是内存问题,因为我在任何调试会话中都没有出现任何 OOM 错误。

现在我明白 Process.waitFor() 需要我从错误流和常规流中刷新缓冲区才能工作,这就是我不使用它的主要原因(我需要输出来管理 VM 特定的错误、证书问题等)。

我想知道是否有人可以向我解释它为什么会挂起,以及是否有办法只使用典型的 readline() 而不会让它挂得那么难。即使脚本应该已经结束了一段时间,它仍然挂起(我尝试使用我在 java 应用程序中使用的完全相同的东西同时运行 java 应用程序和 cmd 命令,让它运行 1 小时,没有任何效果)。它不只是卡在 while 循环中,readline() 是挂起的地方。

这也是一个测试版本,离最终代码还很远,所以请给我留点:这应该是一个常数,这是无用的,等等。我稍后会清理代码。显然,我的代码中的 IP 不是 XX.XX.XX.XXX。

对于如何修复的解释或建议将不胜感激。

顺便说一句,这是我目前使用的脚本:

Add-PSSnapin vmware.vimautomation.core

Connect-VIServer -server $args[0]

Start-VM -VM "MachineTest"

如果您需要更多详细信息,我会尽力提供。

提前感谢您的帮助!

编辑:我之前还使用要求不高的脚本测试了代码,该任务是获取文件内容并打印它。由于无需等待即可获取信息,因此 readline() 运行良好。因此,我相当确定问题出在脚本执行的等待时间上。

另外,请原谅我的错误,英语不是我的主要语言。

提前感谢您的帮助!

EDIT2:因为我无法回答自己的问题:

这是我使用线程后的“最终”代码:

import java.io.*;

public class PowershellTest implements Runnable {

public InputStream is;

public PowershellTest(InputStream newIs){

    this.is = newIs;
}

@Override
public synchronized void run() {
    String input = "";
    String error = "";

    try {


        InputStreamReader inputstreamreader = new InputStreamReader(is);
        BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

        String line;
        while ((line = bufferedreader.readLine()) != null) {
            input += (line + "\n");

        }

        is.close();

    } catch (IOException e) {
        //throw new OperationException("File IO problem.", e);
    }
    System.out.println("Error : " + error + "\nInput : " + input);
}

}

main 简单地创建并启动 2 个线程(PowerShellTest 实例),1 个使用 errorStream,1 个使用 inputStream。

我相信当我第一次编写应用程序并以某种方式修复它时,我犯了一个愚蠢的错误,因为我一遍又一遍地重新编写代码。它仍然需要 5-6 分钟才能运行,如果不比我之前的代码长的话,这在某种程度上是相似的(这是合乎逻辑的,因为在我的例子中,errorStream 和 inputStream 按顺序获取它们的信息)。

无论如何,感谢您的所有回答,尤其是 Miserable Variable 对线程的提示。

【问题讨论】:

  • 我会尝试不关闭outputStream 并在两个不同的并发线程中读取inputStreamerrorStream
  • 我已经尝试过在没有结果的情况下关闭输出流,我将很快尝试使用 StreamGobbler 进行线程处理(实际上在我们输入时进行测试)。此外,输出流没有理由打开,只会带来问题,据我在相当多的帖子上阅读,因为无论如何我没有其他东西要发送。
  • 实际上可行。我开始认为,当我第一次编写应用程序时,我可能只是犯了一个小错误,而我在修改代码时以某种方式修复了该应用程序。仅供参考,我没有使用 StreamGobblers,只有普通的 InputStream。我仍然关闭输出。无论如何,谢谢!
  • 我会将我的评论转换为答案,以便您接受。

标签: java multithreading powershell vmware


【解决方案1】:

首先,在您阅读完信息流之前不要致电waitFor()。我强烈建议您查看ProcessBuilder 而不是简单地使用Runtime.exec,并自己拆分命令而不是依赖Java 为您完成:

ProcessBuilder pb = new ProcessBuilder("powershell", "-file", PATH_TO_SCRIPT,
        SERVER_IP, MACHINE_TO_MOD);
pb.redirectErrorStream(true); // merge stdout and stderr
Process proc = pb.start();

redirectErrorStream 将错误输出合并为正常输出,因此您只需阅读proc.getInputStream()。然后您应该能够在 EOF 之前读取该流,然后调用proc.waitFor()

【讨论】:

  • 我尝试了很多该解决方案(没有redirectErrorStream)。出于某种原因,当我的 errorStream 中明显有错误时,waitFor 不会给我任何错误消息(以及返回值 0)。我相信这是因为我检索到了 VMWARE 错误,而不是 powershell 错误,但我可能错了。无论如何,我没有发现使用 ProcessBuilder 与 Runtime.exec 的行为有任何区别,但我会调查一下。无论如何,谢谢,很多有用的建议!
  • waitFor 返回本机进程的退出代码,所以你是否得到任何有意义的东西取决于你正在执行的程序......
【解决方案2】:

您目前正在等待从inputStream 完成读取,然后再开始从errorStream 读取。如果进程在stdout 之前写入其stderr,那么您可能会陷入僵局。

尝试从同时运行的线程中读取两个流。当你在它的时候,也删除proc.getOutputStream().close();。它不应该影响行为,但也不是必需的。

【讨论】:

  • 根本没有输出/错误,流将永远无法读取任何内容。
猜你喜欢
  • 1970-01-01
  • 2018-03-18
  • 2012-11-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-24
  • 2011-12-15
  • 1970-01-01
  • 2020-09-20
相关资源
最近更新 更多