【问题标题】:End Thread that is stuck in a InputStream.read() loop结束在 InputStream.read() 循环中的线程
【发布时间】:2013-10-09 19:11:52
【问题描述】:

我启动一个 cmd 应用程序,通过这个 SyncPipe Runnable 输出到 System.out:

public class SyncPipe implements Runnable {

    private final InputStream is;
    private final OutputStream os;

    public SyncPipe(InputStream is, OutputStream os) {
        this.is = is;
        this.os = os;
    }

    public void run() {
        try {
            final byte[] buffer = new byte[1024];
            for ( int length = 0; ( length = is.read(buffer) ) != -1; )
                os.write(buffer, 0, length);
            System.out.print("stopped");
        } catch ( Exception ex ) { 
            ex.printStackTrace(); 
        }
    }

}

我用cmd = "C:/bin/read.exe -f D:/test.jpg"启动RunIt

private class RunIt implements Runnable {

    public int p;
    public String cmd;

    public RunIt (int p, String cmd) {
        this.p = p;
        this.cmd = cmd;
    }

    public void run() {
        ProcessBuilder pb = new ProcessBuilder("cmd");
        try {
            process = pb.start();
            (new Thread(new SyncPipe(process.getErrorStream(), System.err))).start();
            (new Thread(new SyncPipe(process.getInputStream(), System.out))).start();
            OutputStream out = process.getOutputStream();
            out.write((cmd + "\r\n").getBytes());
            out.flush();
            out.close();

            try {
                process.waitFor();
            } catch ( InterruptedException e ) {
                e.printStackTrace();
            }

            println("Stopped using %d.", p);
        } catch ( IOException ex ) {
            ex.printStackTrace();
        }
    }

}

我现在的问题是:我怎样才能让(new Thread(new SyncPipe(process.getErrorStream(), System.err))) 死掉?给 SyncPipe 一个布尔变量 stop,在运行时将其设置为 true,然后通过 for ( int length = 0; ( length = is.read(buffer) ) != -1 && !stop; ) 检查它并没有成功。

非常感谢。


我最终完成了@Gray 建议的解决方法。现在可以使用了:

public void run() {
    try {
        final byte[] buffer = new byte[1024];
        do
            if ( is.available() > 0 ) {
                int length = is.read(buffer);
                if ( length != -1 )
                    os.write(buffer, 0, length);
                else
                    stop = true;
            }
        while ( !stop );
    } catch ( Exception ex ) { 
        ex.printStackTrace(); 
    }
}

【问题讨论】:

    标签: java multithreading cmd destroy


    【解决方案1】:

    当底层进程退出时,线程会读取 EOS 并退出。你不必自己做任何特别的事情。

    编辑在我看来,从阅读您的 cmets 到其他答案,您的真正问题正在结束该过程。一旦发生这种情况,这些线程就会解开。你攻击的问题是错误的。

    【讨论】:

    • 你是对的,但他仍然需要关闭流——至少在我的测试程序中没有特定关闭的情况下,后台线程不会在 Unix 上终止。
    • 你们俩都是正确的......我通过Runtime.getRuntime().exec("taskkill /f /pid " + pid); 杀死了底层进程,并且我的旧实现也很好地关闭了。但我绝对需要关闭流。无论如何:非常感谢。
    • @Gray 当然,当他在流上读取 EOS 时,他需要关闭流。我没有说别的。
    • 你说“你不必做任何特别的事情”。如果他使用 process.destroy() 杀死进程(在 Unix 下),线程似乎没有看到 EOS。关闭流是让线程退出的唯一方法。听起来 Windows taskkill 正在做一些不同的事情。
    • @Gray 我有执行此操作的生产代码。我不需要做任何特别的事情,并且 OP 已将答案标记为正确。我无法解释你的观察。也许你应该问自己的问题。
    【解决方案2】:

    InputStream#read() 状态

    此方法阻塞,直到输入数据可用,结束 检测到流,或者抛出异常。

    所以当你进入 for 循环时

    for ( int length = 0; ( length = is.read(buffer) ) != -1; )
        os.write(buffer, 0, length);
    

    直到到达流的末尾,它才能退出,即。该过程停止或您自己关闭流。

    如果SyncPipe 的全部目的是将内容传递给您的标准输出/错误流,您为什么要停止Thread 运行它?

    【讨论】:

    • 我需要在某个时候关闭整个设备。 C:/bin/read.exe 不能很好地关闭(在命令行中执行它时我总是用 ctrl+c 关闭它)。用.destroy() 关闭process 并不能完成这项工作。我希望在需要时关闭所有内容。 out.write(((char)3 + "\r\n").getBytes()); 也无济于事。我怀疑 SyncPipe 线程让一切都保持活力。
    • @dotwin 你需要自己关闭流,调用close()
    • 是的,打印 control-c 不起作用。外壳/操作系统接受该击键并向进程发送信号——它不是处理它的进程@dotwin。
    【解决方案3】:

    我现在的问题:如何让 (new Thread(new SyncPipe(process.getErrorStream(), System.err))) 死掉?

    我相信你将不得不从它下面关闭输入流。我怀疑它在读取时被阻塞了,并且没有设置 stop 变量(即使是正确的 volatile)将使读取线程解除阻塞。

    您将需要执行以下操作:

     InputStream is = process.getInputStream();
     InputStream es = process.getErrorStream();
     ...
     is.close();
     es.close();
    

    代码大致如下所示。我不确定您的waitFor() 电话是否正在返回。

     InputStream is = process.getInputStream();
     InputStream es = process.getErrorStream();
     (new Thread(new SyncPipe(es, System.err))).start();
     (new Thread(new SyncPipe(is, System.out))).start();
     try {
         OutputStream out = process.getOutputStream();
         out.write((cmd + "\r\n").getBytes());
         out.flush();
         out.close();
         try {
             process.waitFor();
         } catch ( InterruptedException e ) {
             e.printStackTrace();
         }
     } finally {
         is.close();
         es.close();
     }
    

    另一个答案可能是在InputStream 上使用available() 方法,这样您就可以循环并检查您的stop 标志。看到这个答案:https://stackoverflow.com/a/1089079/179850

    【讨论】:

    • 我已经尝试过了,希望有一个我可以处理的异常,但什么也没发生。
    • 好的,马上试试……谢谢。
    • 嗯。这对我行得通。您的进程是停止还是试图从另一个线程停止SyncPipe
    • 如果您尝试等待该过程,您可能需要执行synchronized (process) { process.wait(1000); } 之类的操作,然后终止@dotwin 流。
    • 我怀疑你挂在process.waitFor(),对吧?如果您使用process.wait(...) hack,它可能会起作用。
    猜你喜欢
    • 1970-01-01
    • 2020-04-21
    • 1970-01-01
    • 1970-01-01
    • 2020-09-11
    • 1970-01-01
    • 2013-12-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多