【问题标题】:In java, how to make a single thread to wait for a process to finish?在java中,如何让单个线程等待一个进程完成?
【发布时间】:2014-01-30 21:53:13
【问题描述】:

这是我所拥有的: 我有一个启动进程的线程,该进程由 ProcessBuilder 创建并在终端中启动字符串命令。有许多并发线程执行相同的操作,但处理不同的数据。

这是我想做的: 我想让一个线程(它启动一个进程)等待该进程直到它完成。我想出了两种方法,都没有奏效。

方法一:使用 process.waitFor();这会导致所有并发线程等待一个进程(通常是第一个进程)完成。 waitFor() 的描述;说它使单个线程等待,但这不是它所做的,它实际上使所有线程等待。因此程序不再是并发的。

方法 2:运行另一个线程,从该进程中读取管道流,等待直到有流,然后运行应该在该进程之后运行的函数。缺点是现在线程很多,所以我不喜欢使用这种方法。这种方法的另一个问题是,我对应该使用进程的哪些属性感到困惑? OutputStream、InputStream 还是 ErrorStream?

代码如下:

public class Thread1 extends Thread{
private String[] incommand;    //this is the command for the process builder
private String newoutputfile;
InputStream ins = null;
Reader r = null;
BufferedReader br = null;
ProcessBuilder pbtx = null;

public Thread1(String[] incommand, String newoutputfile){
    super("Thread1");
    this.incommand = incommand;        
    this.newoutputfile = newoutputfile;
    this.pbtx = new ProcessBuilder();
}
@Override
public void run(){
    try{                    
                pbtx.command(incommand);                                     
                Process ptx = pbtx.start();
                //to make sure job is done
                ptx.waitFor(); //problem is apparently here
                // made sure job is done
                //the next function is supposed to run after the process is finished                
                    rite();                    
                //
            } catch (IOException ex){
                System.out.println("exception in thread t1");
                ex.printStackTrace();
            }
            catch (InterruptedException yo){
            System.out.println("exception in thread t1");
            }
            }

顺便说一句,该进程是一个 ffmpeg 进程,每个进程都处理不同的视频数据(没有数据依赖关系或竞争条件或这里曾经有过的东西)。所有这些thread1线程都是由另一个函数(main)中的另一个主线程创建和启动的。 Linux中的操作系统。 IDE 是 Netbeans(我从那里得到每个函数的描述)。我试图使复制粘贴的代码尽可能短(为了简单起见),所以如果您认为需要其他功能或程序其余部分的代码,请通知我将它们粘贴在这里。

非常感谢,

【问题讨论】:

  • 你是如何开始你的话题的?我严重怀疑 Process.waitFor() 会冻结所有线程。
  • 谢谢你的回答。线程由主程序中的 threadobject.start() 和 threadobject.join() 启动。不使用 .join() 会导致线程争用它们的输入变量。
  • 好吧,使用.join() 会导致线程等待。 Process.waitFor() 与此无关。

标签: java multithreading process wait


【解决方案1】:

我相信您确实需要使用单独的线程来读取输入;我没有找到避免它的方法。我使用以下算法 (in the H2 database),它对我来说效果很好。另请注意redirectErrorStream

ProcessBuilder pb = new ProcessBuilder();
pb.command(cmd.array());
pb.redirectErrorStream(true);
Process p = pb.start();
copyInThread(p.getInputStream(), quiet ? null : sysOut);
p.waitFor();
return p.exitValue();

private static void copyInThread(final InputStream in, final OutputStream out) {
    new Thread() {
        @Override
        public void run() {
            try {
                while (true) {
                    int x = in.read();
                    if (x < 0) {
                        return;
                    }
                    if (out != null) {
                        out.write(x);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    } .start();
}

【讨论】:

  • 感谢您的回答。稍微修改一下这段代码,我的程序就可以运行了。这里唯一的缺点是现在创建的线程太多。
  • 哦,顺便说一句,我不得不删除那个 p.waitFor();再次因为它导致了老问题,停止了所有线程和进程,除了正在等待的线程和进程。
  • 我认为 p.waitFor() 在这种情况下并不是真正需要的,但奇怪的是我使用它并且从未发现它有任何问题......
【解决方案2】:

使用 Process.waitFor() 的方法 1 应该可以工作。我认为你启动 Thread1 是错误的。

这是一个简单的例子。您可以复制并尝试一下。 StackOverflowPinger 开始一个新的 ping 进程。

public class StackOverflowPinger implements Runnable{

    public StackOverflowPinger(int id){
        this.id = id;
    }

    private int id;

    @Override
    public void run() {
        try {
            ProcessBuilder pbuilder = new ProcessBuilder();
            Process pingprocess = pbuilder.command("ping", "www.stackoverflow.com").start();        
            int pingresult = pingprocess.waitFor();
            if(pingresult == 0)
                System.out.println("Pinger Nr." + this.id + " successfully pinged stackoverflow.");
        } catch (IOException | InterruptedException e) { e.printStackTrace(); }
    }
}

Sleeper 休眠 250 毫秒。

public class Sleeper implements Runnable{

    public Sleeper(int id){
        this.id = id;
    }

    private int id;

    @Override
    public void run() {
        try {
            Thread.sleep(250);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Sleeper " + this.id + " finished sleeping.");
    }
}

这里是实例化和启动线程的 Main。

public class ProcessMain {

    public static void main(String[] args) {
        for(int i = 0 ; i<4; i++){
            new Thread(new StackOverflowPinger(i)).run();
            new Thread(new Sleeper(i)).run();
        }
    }
}

由于 ping 过程花费的时间远远超过 250 毫秒,因此预期的输出应该是这样的。

Sleeper 3 finished sleeping.
Sleeper 0 finished sleeping.
Sleeper 1 finished sleeping.
Sleeper 2 finished sleeping.
Pinger Nr.0 successfully pinged stackoverflow.
Pinger Nr.1 successfully pinged stackoverflow.
Pinger Nr.3 successfully pinged stackoverflow.
Pinger Nr.2 successfully pinged stackoverflow.

但真正的输出是这样的。

Pinger Nr.0 successfully pinged stackoverflow.
Sleeper 0 finished sleeping.
Pinger Nr.1 successfully pinged stackoverflow.
Sleeper 1 finished sleeping.
Pinger Nr.2 successfully pinged stackoverflow.
Sleeper 2 finished sleeping.
Pinger Nr.3 successfully pinged stackoverflow.
Sleeper 3 finished sleeping.

只有当我换行时

new Thread(new StackOverflowPinger(i)).run();
new Thread(new Sleeper(i)).run();

new Thread(new StackOverflowPinger(i)).start();
new Thread(new Sleeper(i)).start();

我得到了预期的输出。所以我认为你使用错误的线程。

【讨论】:

  • 您好,感谢您的详细解答。但是,我以类似的方式启动线程。除了在 .start() 之后我必须 .join() 线程以防止其输入变量出现任何竞争条件。问题依然存在。
猜你喜欢
  • 2021-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-10
  • 2017-09-28
  • 2012-01-07
  • 2010-11-18
相关资源
最近更新 更多