[SOLVED] Leitura muito lenta com Runtime.getRuntime().exec

1 resposta
gmcouto

Olá pessoal. é minha primeira vez aqui

Estou desenvolvendo uma GUI para um programa em C. Como esse programa usa muita saída para reportar status, tive a idéia de chama-lo pela linha de comando através do Java com o Runtime.getRuntime().exec(cmd)... para então fazer a leitura no InputStream do processo de retorno.

O programa gera várias linhas de texto por segundo. E eu preciso ler elas continuamente para mostrar o status para o usuário.

O problema é que minha GUI lê a saída todo engasgado.... demora um tempão para mostrar alguma coisa.... então ele mostra tudo que saiu até ali de uma vez só e engasga denovo.... e tem que esperar mais uns 10 segundos(ou mais) para ver mais alguma saída aparecer.

public class CommandLineRun {
    Process p;
    Runtime rt;
    BufferedReader stdInput, stdError;
    private boolean running;

    public CommandLineRun(File f,String[] args){
        rt = Runtime.getRuntime();
        try {
            p = rt.exec(f.getCanonicalPath(), args, f.getCanonicalFile().getParentFile());

            running=true;
        } catch (IOException ex) {
            Logger.getLogger(CommandLineRun.class.getName()).log(Level.SEVERE, null, ex);
        }
        stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
        stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
    }
    public BufferedReader getOutput(){
        return stdInput;
    }
    public BufferedReader getError(){
        return stdError;
    }
    public void halt(){
        p.destroy();
        running=false;
    }
    public boolean procDone() {
        try {
            int v = p.exitValue();
            running=false;
            return true;
        }
        catch(IllegalThreadStateException e) {
            return false;
        }
    }
    public boolean isRunning() {
        return running;
    }
}
public class monExatoModuleOut extends Thread {

    CommandLineRun run;

    public monExatoModuleOut(CommandLineRun run) {
        this.run = run;
    }

    public void run() {
        String s;
        while (!run.procDone() && run.isRunning()) {
            try {
                while (( s = run.getError().readLine()) != null) {
                    System.out.println(s);
                }
            } catch (IOException ex) {
                Logger.getLogger(execExatoModuleThread.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }
}

O fato é que o programa que eu tenho que rodar faz uso intensivo de CPU, além leitura e escrita de dados. O meu medo é que devido a isso a thread do java que coloca os dados no InputStream esteja comprometida por causa disso... ficando engasgada por um tempão.

Alguém tem uma idéia de como solucionar isso?

1 Resposta

gmcouto

Depois de pesquisar muito, e fazer muito debug… constatei que não cometi erro nenhum.

O problema é que no programa em C que eu chamo via Runtime nunca faz flush na saída… e aí o Java fica enchendo todo buffer para depois me dar controle no InputStream.

Exemplo:

#include <iostream> int main(){ long a = 0; long b = 0; long c = 1; while(true){ std::cout<<"Oi "<<c<<"\n";//nunca faça isso while(a<1000000){ a++; } a=0; while(b<1000000){ b++; } b=0; c++; std::cout << "\n" << std::flush; //pode ser isso std::cout << endl; // ou isso } }

Quando você quebra a linha apenas com “\n” o buffer não é limpo, então o Java simplismente continua esperando saídas até completar o buffer.
Quando você chama a quebra endl ou o próprio flush, o Java pega os dados coletados e joga no InputStream… te liberando o uso para leitura de dados.

Ou seja, tenho que solicitar para o cara modificar o programa dele, ou eu nunca vou ter o comportamento que quero na minha GUI.

Criado 13 de maio de 2010
Ultima resposta 14 de mai. de 2010
Respostas 1
Participantes 1