- PR -

WinXP で InputStream の読取にタイムラグが生じる

1
投稿者投稿内容
でつ
会議室デビュー日: 2006/03/28
投稿数: 4
お住まい・勤務地: 東京
投稿日時: 2006-03-28 17:22
お世話になります。
WinXP上でJavaから他のプログラムを起動し、その出力を読み取るプログラムを作っているのですが、その外部プログラムが終了するまでInputStreamのReadが戻ってこない状況になっています。
ソースを見ていただけると分かると思うのですが、起動して20秒たたないと何も表示されない状況になります。

Javaのソース: ExecAt1.java
import java.io.*;
public class ExecAt1 {
public static void main(String[] args) throws Exception
{
String cmd = "cmd /c D:\\\\temp\\\\test.exe";
byte[] ibuff = new byte[100];
int ilen, alen;

Process run = Runtime.getRuntime().exec(cmd);
InputStream is = run.getInputStream();

System.out.println("Start\\n");
while(true){
ilen = is.read(ibuff);
if( ilen < 0 ) break;
System.out.write(ibuff,0,ilen);
}
System.out.println("End\\n");
}
}

外部プログラムのサンプル:test.c
#include <stdio.h>

int main()
{
int i;

for(i=0;i<10;i++){
printf("%d ======================\\n",i);
sleep(2);
}
exit(0);
}
山本 裕介
ぬし
会議室デビュー日: 2003/05/22
投稿数: 2415
お住まい・勤務地: 恵比寿
投稿日時: 2006-03-28 17:35
出力側がバッファリングされているのではないでしょうか?
だとしたら flush してあげましょう。
でつ
会議室デビュー日: 2006/03/28
投稿数: 4
お住まい・勤務地: 東京
投稿日時: 2006-04-04 10:41
このJavaから起動する予定のプログラムは実はPerlスクリプトで、
#! /usr/bin/perl
$|=1;
のように autoflush を用いても解決できませんでした。
同じソースでも Linux では動作するので、Java というより WinXP の差異によるものだと判断しました。
ですのでJavaとしてではなくWindows関連の会議室の方へ再度投稿します。
ありがとうございました。
未記入
ぬし
会議室デビュー日: 2004/09/17
投稿数: 667
投稿日時: 2006-04-04 12:33
read() が返ってこないというのはどうやって確認しましたか?
System.out.write() ではコンソールへの出力はバッファリングされますから、flush() しないと read() が返ってきたかどうか確認にならないと思います。

私の推測では、read() は 10回目までの呼び出しまで正しく返ってきています。ただ、System.out.write() での確認出力がバッファリングされているために、それが確認できていない。また、read() の 11回目の出力はブロックされているでしょう。それは相手プロセスが標準出力(stdout)を閉じていないから、当然といえば当然かと。むしろ、Linux では(プロセス終了前に)標準出力から EOF が返ってくるほうが挙動としては解せないです。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2006-04-04 14:55
まず、Perl でも C で書いた EXE でも現象が同じなのですよね。
また、
引用:

ycosさんの書き込み (2006-03-28 17:22) より:
String cmd = "cmd /c D:\\temp\\test.exe";
byte[] ibuff = new byte[100];
int ilen, alen;

Process run = Runtime.getRuntime().exec(cmd);


を見ると、シェル(cmd.exe)を使う必要がありませんので、使わないほうが余計な要素が減って良いと思います。
また、exec メソッドは、引数に空白区切りの文字列を渡すものと、文字列の配列で渡すものがあります。通常は、後者のほうが確実です(たとえば EXE に渡す引数がパス名で、それに空白がある場合等の場合等に顕著)。

flush が効いているのか怪しい場合は、とりあえず、ムダに長めの文字列を100KBほど出力してみるようにしてみてはどうでしょうか。
PAL
ベテラン
会議室デビュー日: 2002/11/14
投稿数: 63
投稿日時: 2006-04-04 21:52
引用:

ycosさんの書き込み (2006-03-28 17:22) より:
お世話になります。
WinXP上でJavaから他のプログラムを起動し、その出力を読み取るプログラムを作っているのですが、その外部プログラムが終了するまでInputStreamのReadが戻ってこない状況になっています。




http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=25599&forum=12
私の以前の投稿ですが参考になりませんでしょうか?
別スレッドにしないと、標準出力がパンクしていたような。
coasm
大ベテラン
会議室デビュー日: 2001/11/26
投稿数: 237
投稿日時: 2006-04-04 23:39
test.exe の標準出力がバッファリングされているのが原因です。
1行出力する毎にJava側で1行readしたいのなら、printf()する毎にfflush(stdout); する必要があります。
perl なら、$| = 1; で解決できます。

Java側のSystem.out は(出力先がコンソールなら)autoflushになっているので、
改めてflushする必要はありません。

ただ、標準出力の先がパイプの場合にフルバッファリングになるのは
Windowsでもunix系でも同じハズなので、linuxで違う挙動をしめすというのは解せません。

引用:

#! /usr/bin/perl
$|=1;


この書き方がWindowsで通用するはずないのですが、どうやって確認されたのでしょう?
もしかして、Cygwinですか?
1

スキルアップ/キャリアアップ(JOB@IT)