2014-01-30 2 views
0

Вот что у меня есть: У меня есть поток, который запускает процесс, этот процесс создается ProcessBuilder и запускает строчную команду в терминале. Существует много параллельных потоков, которые делают то же самое, но на разных данных.В java, как сделать ничью, чтобы ждать завершения процесса?

Вот что я хочу сделать: Я хочу создать ничью (которая начала процесс), чтобы дождаться этого процесса, пока он не закончится. Я придумал два метода, и никто не работает.

способ 1: использовать 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 создаются и запускаются другим основным потоком в другой функции (основной). Операционная система в Linux. IDE - Netbeans (вот где я получаю описание каждой функции). Я попытался сохранить скопированный код как можно короче (для простоты), поэтому, если вы считаете, что код для других функций или остальной программы необходим, пожалуйста, сообщите мне, чтобы они вставляли их здесь.

Большое спасибо,

+0

Как вы начинаете? Я серьезно сомневаюсь, что Process.waitFor() замораживает все потоки. – Kayaman

+0

Поговорите с вами для ответа. Потоки запускаются threadobject.start(), а затем threadobject.join() в основной программе. Не использовать .join() приведет к тому, что потоки будут проходить по их входным переменным. – user2452253

+0

Ну, использование '.join()' приведет к тому, что потоки будут ждать. 'Process.waitFor()' не имеет к этому никакого отношения. – Kayaman

ответ

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(); 
} 
+0

Благодарим вас за ответ. С небольшим изменением этого кода моя программа работает сейчас. Единственный недостаток здесь - слишком много потоков, созданных прямо сейчас. – user2452253

+0

О, и BTW мне пришлось удалить этот p.waitFor(); снова потому, что он вызывает старую проблему, останавливая все потоки и процессы, кроме того, кто ждет. – user2452253

+0

Я думаю, что p.waitFor() на самом деле не нужен в этом случае, но странно, что я использую его и никогда не видел никаких проблем с ним ... –

0

Метод 1 с Process.waitFor() должен работать. Я думаю, что вы начинаете 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."); 
    } 
} 

И вот Главная, которая создает и запускает потоки.

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(); 

я получить ожидаемый результат. Поэтому я думаю, что вы неправильно используете свои потоки.

+0

Здравствуйте, спасибо за подробный ответ. Тем не менее, я запускаю потоки аналогичным образом. За исключением того, что после .start() я должен использовать .join() потоки, чтобы предотвратить любое состояние гонки над их входными переменными. Проблема все еще сохраняется. – user2452253

Смежные вопросы