- PR -

コマンド終了待ち時のタイムアウト処理

投稿者投稿内容
finch
常連さん
会議室デビュー日: 2006/09/29
投稿数: 26
投稿日時: 2006-09-29 11:43
javaからコマンドを発行してコマンド終了を待つ処理において、タイムアウトの設定ができないかを検討していまして、以下のように監視スレッドを生成する方法を考えました。
(監視スレッドでタイムアウト時間分sleepして、sleepが終わると、コマンド発行側にinterruptをかけます)

−−−−−−−−−コマンド発行側−−−−−−−−−−−−−
Thread kansi = new Kansi( Thread.currentThread() );
kansi.start();

String cmdExtract[] = {"xxxx", "yyy"};
Process process= Runtime.getRuntime().exec(cmdExtract);

try {
int code = process.waitFor();
kansi.interrupt();
}
catch(InterruptedException e) {
System.out.println("timeout");
}
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

−−−−−−−−−監視スレッド−−−−−−−−−−−−−−
class Kansi extends Thread {

private Thread parent;

public Kansi(Thread parent) {
this.parent = parent;
}

public void run() {
try {
Thread.sleep(timeoutLimit);
parent.interrupt();
}
catch(InterruptedException e) {
}
}

}
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−

この方法でほぼ問題は無いと考えておりますが、「監視スレッドからのinterruptが、コマンド発行側のtry,catchの後で効く事が無いか」ちょっと気になっています。
もしそのケースがあるとなると、コマンド発行側において、コマンド処理が終わった後、予期しない所でinterruptが効いてしまう事になります。

このあたりについて、情報をお持ちの方がいらっしゃいましたら、ご指導お願いいたします。

なお、JDKは5.0です。
squeak
会議室デビュー日: 2005/05/31
投稿数: 6
投稿日時: 2006-10-01 18:39
「コマンド発行側」も
「専用の」スレッドを使うのはどうでしょう。


コード:
/*外部プロセス用スレッド*/

class ProcessThread extends Thread{
public void run(){
process=Runtime.getRuntime().exec(cmd);

//プロセス終了を待機
try {
process.waitFor();
}catch(InterruptedException e){
process.destroy();
}
}
}

/*監視スレッド*/
class Watch extends Thread{
Thread target;
long timeout;

Watch(Thread target,long timeout){
this.target=target;
this.timeout=timeout;
}

public void run(){
try{
Thread.sleep(timeout);
target.interrupt();
}catch(InterruptedException e){}
}
}

---使い方---
ProcessThread pt=new ProcessThread();
Watch watch=new Watch(pt,5000);
pt.start();
watch.start();



[ メッセージ編集済み 編集者: squeak 編集日時 2006-10-01 20:09 ]
finch
常連さん
会議室デビュー日: 2006/09/29
投稿数: 26
投稿日時: 2006-10-02 17:53
返信ありがとうございます。
確かに専用スレッドを作る事で、「予期しない所でinterrutが効いてしまう」危険性は、避けられるように思います。
逆に言うと、最初のコーディングでは、その危険性は避けられないという事でしょうか?
もしそのあたりについて、ご存知の方がいらっしゃれば、指摘頂ければ幸いです。
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2006-10-02 18:38
引用:

finchさんの書き込み (2006-10-02 17:53) より:
逆に言うと、最初のコーディングでは、その危険性は避けられないという事でしょうか?
もしそのあたりについて、ご存知の方がいらっしゃれば、指摘頂ければ幸いです。


可能だとは思いますよ。面倒でも良ければ、
スレッド間で二つのオブジェクトのwait()/notify()を駆使して。

コード:

Object 起動イベント = new Object();
Object 完了イベント = new Object();
volatile boolean completed = false;

--------------------------------
プロセス起動スレッド
--------------------------------

synchronized(起動イベント) {
  監視用スレッドを起動
  起動イベント.wait();
}
プロセスを起動して待機
synchronized (完了イベント) {
  completed = true;
  完了イベント.notify();
}

--------------------------------
タイムアウト監視スレッド
--------------------------------

synchronized (完了イベント) {
  synchronized(起動イベント) {
    起動イベント.notify();
  }
  完了イベント.wait(タイムアウト);
  if (!completed) {
    プロセス起動スレッド.interrupt();
  }
}



これならタイムアウト監視スレッドからのinterrupt()の発生が
「起動イベント.wait()」と「プロセスを起動して待機」の
どちらかの待機状態でのみ発生する事を保証できます。

#あまり細かく検証していないのでツッコミ歓迎…。

他には、java.util.concurrent.CyclicBarrierなどのクラスで可能かもしれないし、
java.util.concurrent.ExecutorServiceにプロセス起動をsubmit()して、
Futureをタイムアウト付きで待機する方法がスマートかもしれません。
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2006-10-02 21:54
以下のようなコードはいかがでしょう。
厳密な検証はしていませんが。
コード:
new Thread() {
	public void run() {
		try {
			//ANY TASK
		} catch (InterruptedException e) {
		}
	}
	public synchronized void start() {
		super.start();
			try {
			join(1500);
		} catch (InterruptedException e) {
		}
		if (isAlive()) {
			interrupt();
		}
	}
}.start();


スレッドはあまり得意ではないので、ツッコミがあればよろしくです。
nagise
ぬし
会議室デビュー日: 2006/05/19
投稿数: 1141
投稿日時: 2006-10-02 23:07
ソースをレビューした観想としてはあしゅさんのコードが安全そう。

かつのりのコードはisAlive()〜interrupt()の間に
"ANY TASK"部分を抜けるケースがありそうな気がするなぁ。
try-catchブロック以後になんにもコードがなければ
まずは発生しないとは思うのですが。
そこだけ同期すれば確実なのかな。
だとしてもこの方式が比較的すっきりまとまりそうな気もするなぁ。
あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2006-10-03 10:49
確かに。プロセス起動側を別スレッドにすれば簡単ですね。

引用:

finchさんの書き込み (2006-10-02 17:53) より:
逆に言うと、最初のコーディングでは、その危険性は避けられないという事でしょうか?
もしそのあたりについて、ご存知の方がいらっしゃれば、指摘頂ければ幸いです。



この書き込みを見てそのまま回答したので、
プロセス起動を既存のスレッドで実装する方法しか考えなかったです。

引用:

nagiseさんの書き込み (2006-10-02 23:07) より:
"ANY TASK"部分を抜けるケースがありそうな気がするなぁ。
try-catchブロック以後になんにもコードがなければ
まずは発生しないとは思うのですが。



起動したプロセスの完了が寿命なスレッドなら割り込んでも安全なのでは?
終了したスレッドにinterrupt()するのなら実害なさそうに思うので。
Javadocには記述を見つけられなかったので確証はないですけど。
finch
常連さん
会議室デビュー日: 2006/09/29
投稿数: 26
投稿日時: 2006-10-03 14:40
様々な案を返信ありがとうございます。
確かに、かつのりさんの案のように、プロセス起動側を別スレッドにする方法は、全く考えつきませんでした。
ただ、start() メソッドは、synchronizedが必要無いように思うのですが、如何でしょうか?
それから、あしゅさんが言われるように、私が実験した所では、終了したスレッドにinterrupt()しても実害は無いようでした。

なお、あしゅさんが「2006-10-02 18:38」に投稿された方法は、デッドロックになるのではないでしょうか?
(プロセス起動スレッドが「起動イベント」を確保している間に、タイムアウト監視スレッドからのnotifyを待つ。
タイムアウト監視スレッドは、notify発行の前に、「起動イベント」を確保できない。)

いずれにしろ、プロセス起動側を別スレッドにするやり方が、比較的シンプルで安全な方法のように思われました。
いろいろ、ありがとうございました。

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