Java에서 여러 개의 스트림을 어떻게 처리합니까?
문제
프로세스를 실행하고 입력, 출력 및 오류 스트림으로 작업을 수행하려고합니다. 이것을하는 명백한 방법은 select()
,하지만 내가 자바에서 찾을 수있는 유일한 것은 Selector.select()
, 그것은 a Channel
. a를 얻는 것은 불가능한 것 같습니다 Channel
an InputStream
또는 OutputStream
(FileStream
a getChannel()
방법이지만 여기서는 도움이되지 않습니다)
대신, 나는 모든 스트림을 투표하기 위해 몇 가지 코드를 썼습니다.
while( !out_eof || !err_eof )
{
while( out_str.available() )
{
if( (bytes = out_str.read(buf)) != -1 )
{
// Do something with output stream
}
else
out_eof = true;
}
while( err_str.available() )
{
if( (bytes = err_str.read(buf)) != -1 )
{
// Do something with error stream
}
else
err_eof = true;
}
sleep(100);
}
결코 종료되지 않는다는 점을 제외하고는 작동합니다. 스트림 중 하나가 파일 끝에 도달하면 available()
0을 반환합니다 read()
호출되지 않으며 EOF를 나타내는 -1 리턴을 얻지 못합니다.
하나의 솔루션은 EOF를 감지하는 비 블로킹 방법입니다. 문서에서 어디서나 볼 수 없습니다. 또는 내가하고 싶은 일을하는 더 좋은 방법이 있습니까?
나는 여기 에이 질문을 본다 :링크 텍스트그리고 그것이 내가 원하는 것을 정확하게 수행하지는 않지만, 나는 아마도 내가 가진 특정 문제에 대해 각 스트림에 대해 별도의 스레드를 산란하는 것으로 아마도 그 아이디어를 사용할 수 있습니다. 그러나 확실히 그렇게 할 수있는 유일한 방법은 없습니까? 분명히 각각의 스레드를 사용하지 않고 여러 스트림에서 읽을 수있는 방법이 있어야합니까?
해결책
당신이 말했듯이, 해결책 이 답변에 설명되어 있습니다 프로세스에서 stdout과 stderr를 모두 읽는 전통적인 방법입니다. 스레드마다 스레드는 약간 성가시더라도 갈 길입니다.
다른 팁
실제로 모니터링하려는 각 스트림마다 스레드를 산란하는 경로를 가야합니다. 유스 케이스가 문제의 프로세스의 stdout과 stderr를 결합 할 수있는 경우 하나의 스레드 만 필요합니다. 그렇지 않으면 두 스레드가 필요합니다.
외부 프로세스를 시작하고 출력을 취하고 동시에 오류 및 프로세스 종료를 찾고 종료 할 수있는 동안 작업을 수행 해야하는 프로젝트 중 하나에서 제대로 얻는 데 시간이 걸렸습니다. Java 앱의 사용자가 작업을 취소 할 때.
런 () 메소드가 다음과 같은 모습을 보이는 시청 부분을 캡슐화하기 위해 다소 간단한 클래스를 만들었습니다.
public void run() {
BufferedReader tStreamReader = null;
try {
while (externalCommand == null && !shouldHalt) {
logger.warning("ExtProcMonitor("
+ (watchStdErr ? "err" : "out")
+ ") Sleeping until external command is found");
Thread.sleep(500);
}
if (externalCommand == null) {
return;
}
tStreamReader =
new BufferedReader(new InputStreamReader(watchStdErr ? externalCommand.getErrorStream()
: externalCommand.getInputStream()));
String tLine;
while ((tLine = tStreamReader.readLine()) != null) {
logger.severe(tLine);
if (filter != null) {
if (filter.matches(tLine)) {
informFilterListeners(tLine);
return;
}
}
}
} catch (IOException e) {
logger.logExceptionMessage(e, "IOException stderr");
} catch (InterruptedException e) {
logger.logExceptionMessage(e, "InterruptedException waiting for external process");
} finally {
if (tStreamReader != null) {
try {
tStreamReader.close();
} catch (IOException e) {
// ignore
}
}
}
}
호출쪽에는 다음과 같습니다.
Thread tExtMonitorThread = new Thread(new Runnable() {
public void run() {
try {
while (externalCommand == null) {
getLogger().warning("Monitor: Sleeping until external command is found");
Thread.sleep(500);
if (isStopRequested()) {
getLogger()
.warning("Terminating external process on user request");
if (externalCommand != null) {
externalCommand.destroy();
}
return;
}
}
int tReturnCode = externalCommand.waitFor();
getLogger().warning("External command exited with code " + tReturnCode);
} catch (InterruptedException e) {
getLogger().logExceptionMessage(e, "Interrupted while waiting for external command to exit");
}
}
}, "ExtCommandWaiter");
ExternalProcessOutputHandlerThread tExtErrThread =
new ExternalProcessOutputHandlerThread("ExtCommandStdErr", getLogger(), true);
ExternalProcessOutputHandlerThread tExtOutThread =
new ExternalProcessOutputHandlerThread("ExtCommandStdOut", getLogger(), true);
tExtMonitorThread.start();
tExtOutThread.start();
tExtErrThread.start();
tExtErrThread.setFilter(new FilterFunctor() {
public boolean matches(Object o) {
String tLine = (String)o;
return tLine.indexOf("Error") > -1;
}
});
FilterListener tListener = new FilterListener() {
private boolean abortFlag = false;
public boolean shouldAbort() {
return abortFlag;
}
public void matched(String aLine) {
abortFlag = abortFlag || (aLine.indexOf("Error") > -1);
}
};
tExtErrThread.addFilterListener(tListener);
externalCommand = new ProcessBuilder(aCommand).start();
tExtErrThread.setProcess(externalCommand);
try {
tExtMonitorThread.join();
tExtErrThread.join();
tExtOutThread.join();
} catch (InterruptedException e) {
// when this happens try to bring the external process down
getLogger().severe("Aborted because auf InterruptedException.");
getLogger().severe("Killing external command...");
externalCommand.destroy();
getLogger().severe("External command killed.");
externalCommand = null;
return -42;
}
int tRetVal = tListener.shouldAbort() ? -44 : externalCommand.exitValue();
externalCommand = null;
try {
getLogger().warning("command exit code: " + tRetVal);
} catch (IllegalThreadStateException ex) {
getLogger().warning("command exit code: unknown");
}
return tRetVal;
불행히도 나는 독립적 인 실행 가능한 예를들을 필요는 없지만 아마도 이것이 도움이 될 것입니다. 다시해야한다면 스스로 제작 한 스톱 플래그 대신 thread.interrupt () 메소드를 사용하는 것을 다시 살펴볼 것입니다 (휘발성을 선언 할 마음이 있습니다!). :)