为什么队列不在主线程上更新?
Why isn't the queue updating on the main thread?
我正在打开一个 PowerShell 实例并通过 System.in 向它发送命令,我想将输出收集到一个队列中,这样我就可以在不干扰命令输入的情况下打印结果。然而,队列并没有在主线程上填满,在主线程上休眠只能稍微解决这个问题。我想问题是没有同步队列,但我不确定如何。
package PWST;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class PWST {
public static void main(String[] args) {
try {
Scanner in = new Scanner(System.in);
System.out.print("3[H3[2J > "); // clear console.
String[] tokens = in.nextLine().split(",");
Queue<String> stdout = new LinkedList<>();
Queue<String> stderr = new LinkedList<>();
Process process = Runtime.getRuntime().exec("powershell.exe");
new Thread(new SPQ(process.getInputStream(), stdout)).start();
new Thread(new SPQ(process.getErrorStream(), stderr)).start();
PrintWriter out = new PrintWriter(process.getOutputStream());
for (String token : tokens) // parse commands (will be expanded to continuously ask for input)
out.println(token.strip());
// print powershell output after:
System.out.println(stdout);
System.out.println(stderr);
in.close();
out.close();
process.waitFor();
System.out.println("Shell link closed");
} catch (Exception e) { e.printStackTrace(); }
}
}
package PWST;
import java.io.InputStream;
import java.util.Queue;
class SPQ implements Runnable {
private Queue<String> queue;
private InputStream istrm;
public SPQ(InputStream istrm, Queue<String> queue) {
this.istrm = istrm;
this.queue = queue;
}
public void run() {
try {
final byte[] buffer = new byte[1024];
for (int length = 0; (length = istrm.read(buffer)) != -1; )
queue.add(new String(buffer, 0, length)); // store output in queue
} catch (Exception e) { e.printStackTrace(); }
}
}
至少有两个问题。
首先,关闭 Scanner
和 PrintWriter
也会关闭它们的不死流(在这种情况下,Scanner
正在从标准输入读取)。这是不可取的,因为,首先,您永远不想关闭 stdin 流,其次,该过程可能尚未完成对流的处理,但更重要的是,我会非常小心地关闭您没有自己创建的流,只是说.
第二个问题是,您试图在 SPQ
实际有时间处理输出之前读取(流的)输出,例如,如果我调整您的代码并使用。 ..
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.Queue;
public final class Main {
public static void main(String[] args) throws IOException, InterruptedException {
new Main();
}
public Main() throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("bash", "-c", "ls");
pb.redirectErrorStream(true);
System.out.println(">> Start process");
Process p = pb.start();
Queue<String> stdout = new LinkedList<>();
Thread thread = new Thread(new SPQ(p.getInputStream(), stdout));
System.out.println(">> Start thread");
thread.start();
System.out.println(">> Waiting for process to exit");
p.waitFor();
System.out.println("<< Process has exited");
System.out.println(stdout);
}
class SPQ implements Runnable {
private Queue<String> queue;
private InputStream istrm;
public SPQ(InputStream istrm, Queue<String> queue) {
this.istrm = istrm;
this.queue = queue;
}
public void run() {
System.out.println(">> Started reading stream");
try {
final byte[] buffer = new byte[1024];
for (int length = 0; (length = istrm.read(buffer)) != -1;) {
queue.add(new String(buffer, 0, length)); // store output in queue
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("<< Done reading stream");
}
}
}
它将输出...
>> Start process
>> Start thread
>> Waiting for process to exit
<< Process has exited
>> Started reading stream
[]
<< Done reading stream
如您所见,我在线程完成流处理之前打印了 Queue
的内容。
如果我在 p.waitFor();
之后添加 thread.join();
它将打印...
>> Start process
>> Start thread
>> Waiting for process to exit
>> Started reading stream
<< Done reading stream
<< Process has exited
[contents
of
my
working
directory
which
is
not
very
interesting]
可运行示例...
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.Queue;
public final class Main {
public static void main(String[] args) throws IOException, InterruptedException {
new Main();
}
public Main() throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("bash", "-c", "ls");
pb.redirectErrorStream(true);
System.out.println(">> Start process");
Process p = pb.start();
Queue<String> stdout = new LinkedList<>();
Thread thread = new Thread(new SPQ(p.getInputStream(), stdout));
System.out.println(">> Start thread");
thread.start();
System.out.println(">> Waiting for process to exit");
p.waitFor();
thread.join();
System.out.println("<< Process has exited");
System.out.println(stdout);
}
class SPQ implements Runnable {
private Queue<String> queue;
private InputStream istrm;
public SPQ(InputStream istrm, Queue<String> queue) {
this.istrm = istrm;
this.queue = queue;
}
public void run() {
System.out.println(">> Started reading stream");
try {
final byte[] buffer = new byte[1024];
for (int length = 0; (length = istrm.read(buffer)) != -1;) {
queue.add(new String(buffer, 0, length)); // store output in queue
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("<< Done reading stream");
}
}
}
请注意 - 我不是 运行 Windows 所以我无法测试 Powershell
我已经完成了我能想到的最小示例,所以我没有向流中写入内容,但从概念上讲,这解决了您似乎遇到的问题
我正在打开一个 PowerShell 实例并通过 System.in 向它发送命令,我想将输出收集到一个队列中,这样我就可以在不干扰命令输入的情况下打印结果。然而,队列并没有在主线程上填满,在主线程上休眠只能稍微解决这个问题。我想问题是没有同步队列,但我不确定如何。
package PWST;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class PWST {
public static void main(String[] args) {
try {
Scanner in = new Scanner(System.in);
System.out.print("3[H3[2J > "); // clear console.
String[] tokens = in.nextLine().split(",");
Queue<String> stdout = new LinkedList<>();
Queue<String> stderr = new LinkedList<>();
Process process = Runtime.getRuntime().exec("powershell.exe");
new Thread(new SPQ(process.getInputStream(), stdout)).start();
new Thread(new SPQ(process.getErrorStream(), stderr)).start();
PrintWriter out = new PrintWriter(process.getOutputStream());
for (String token : tokens) // parse commands (will be expanded to continuously ask for input)
out.println(token.strip());
// print powershell output after:
System.out.println(stdout);
System.out.println(stderr);
in.close();
out.close();
process.waitFor();
System.out.println("Shell link closed");
} catch (Exception e) { e.printStackTrace(); }
}
}
package PWST;
import java.io.InputStream;
import java.util.Queue;
class SPQ implements Runnable {
private Queue<String> queue;
private InputStream istrm;
public SPQ(InputStream istrm, Queue<String> queue) {
this.istrm = istrm;
this.queue = queue;
}
public void run() {
try {
final byte[] buffer = new byte[1024];
for (int length = 0; (length = istrm.read(buffer)) != -1; )
queue.add(new String(buffer, 0, length)); // store output in queue
} catch (Exception e) { e.printStackTrace(); }
}
}
至少有两个问题。
首先,关闭 Scanner
和 PrintWriter
也会关闭它们的不死流(在这种情况下,Scanner
正在从标准输入读取)。这是不可取的,因为,首先,您永远不想关闭 stdin 流,其次,该过程可能尚未完成对流的处理,但更重要的是,我会非常小心地关闭您没有自己创建的流,只是说.
第二个问题是,您试图在 SPQ
实际有时间处理输出之前读取(流的)输出,例如,如果我调整您的代码并使用。 ..
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.Queue;
public final class Main {
public static void main(String[] args) throws IOException, InterruptedException {
new Main();
}
public Main() throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("bash", "-c", "ls");
pb.redirectErrorStream(true);
System.out.println(">> Start process");
Process p = pb.start();
Queue<String> stdout = new LinkedList<>();
Thread thread = new Thread(new SPQ(p.getInputStream(), stdout));
System.out.println(">> Start thread");
thread.start();
System.out.println(">> Waiting for process to exit");
p.waitFor();
System.out.println("<< Process has exited");
System.out.println(stdout);
}
class SPQ implements Runnable {
private Queue<String> queue;
private InputStream istrm;
public SPQ(InputStream istrm, Queue<String> queue) {
this.istrm = istrm;
this.queue = queue;
}
public void run() {
System.out.println(">> Started reading stream");
try {
final byte[] buffer = new byte[1024];
for (int length = 0; (length = istrm.read(buffer)) != -1;) {
queue.add(new String(buffer, 0, length)); // store output in queue
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("<< Done reading stream");
}
}
}
它将输出...
>> Start process
>> Start thread
>> Waiting for process to exit
<< Process has exited
>> Started reading stream
[]
<< Done reading stream
如您所见,我在线程完成流处理之前打印了 Queue
的内容。
如果我在 p.waitFor();
之后添加 thread.join();
它将打印...
>> Start process
>> Start thread
>> Waiting for process to exit
>> Started reading stream
<< Done reading stream
<< Process has exited
[contents
of
my
working
directory
which
is
not
very
interesting]
可运行示例...
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import java.util.Queue;
public final class Main {
public static void main(String[] args) throws IOException, InterruptedException {
new Main();
}
public Main() throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder("bash", "-c", "ls");
pb.redirectErrorStream(true);
System.out.println(">> Start process");
Process p = pb.start();
Queue<String> stdout = new LinkedList<>();
Thread thread = new Thread(new SPQ(p.getInputStream(), stdout));
System.out.println(">> Start thread");
thread.start();
System.out.println(">> Waiting for process to exit");
p.waitFor();
thread.join();
System.out.println("<< Process has exited");
System.out.println(stdout);
}
class SPQ implements Runnable {
private Queue<String> queue;
private InputStream istrm;
public SPQ(InputStream istrm, Queue<String> queue) {
this.istrm = istrm;
this.queue = queue;
}
public void run() {
System.out.println(">> Started reading stream");
try {
final byte[] buffer = new byte[1024];
for (int length = 0; (length = istrm.read(buffer)) != -1;) {
queue.add(new String(buffer, 0, length)); // store output in queue
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("<< Done reading stream");
}
}
}
请注意 - 我不是 运行 Windows 所以我无法测试 Powershell
我已经完成了我能想到的最小示例,所以我没有向流中写入内容,但从概念上讲,这解决了您似乎遇到的问题