取消 Eclipse 插件作业

Cancel eclipse plugin job

我 运行 在 eclipse 插件中 Job 一个执行长操作而没有太多输出的进程。
如果用户请求,我希望能够取消 Job,但是通过下面的实现,Job 不会停止,除非进程将某些内容打印到输出流。

Process process = new ProcessBuilder(args).start();
Scanner scanner = new Scanner(
                (new InputStreamReader(
                        process.getInputStream(), UTF_8_CHARSET)));

while (scanner.hasNext())
{

    if (monitor.isCanceled() {
        // user canceled the job, destroy the process to return
        process.destroy();
        break;
    }

    CONSOLE.getStream().println(scanner.nextLine());

}

try {
    process.waitFor();

} catch (InterruptedException e) {

    Activator.log(ERROR, e.getMessage(), e);

} finally {
    process.destroyForcibly();
}

我是否有其他选项来处理作业的取消并更快地停止进程而不是等待新的换行?

而不是 Scanner 使用 BufferedReader 提供 ready 方法,这是一种 non-blocking 告诉你是否有东西要阅读而不是使用的方法扫描器的 nextLine() 在实际读取某些内容之前一直处于阻塞状态。

小示例实现:

volatile static boolean stop = false;

public static void main(String[] args) throws InterruptedException
{
    // Create a new thread that reads input from System.in
    new Thread(() -> {

        BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
        while (!stop)
        {
            try
            {
                // If there is something to read read it and print it
                if (read.ready())
                {
                    System.out.println(read.readLine());
                }
            }
            catch (IOException e)
            {
                // Do some handling...
            }
        }

    }).start();

    // Wait 5 seconds then stop the thread by making the flag false.
    Thread.sleep(5000);
    stop = true;
}

你的flag显然是process提供的。要点是使用 non-blocking 操作(查看是否确实有要打印的内容)而不是使用阻塞操作。

您应该将读取进程输出流的代码放到一个单独的线程中,并在您的主循环中等待进程以短暂的超时结束,这样您就可以检查是否已取消。

所以主要代码应该是这样的:

Process process = new ProcessBuilder(args).start();

// Read standard output
new StreamConsumer(process.getInputStream()).start();

// You should also always read stderr
new StreamConsumer(process.getErrorStream()).start();

// Wait for process to end and check for cancel

while (!process.waitFor(100, TimeUnit.MILLISECONDS) {
  if (monitor.isCanceled() {
    // user canceled the job, destroy the process to return
    process.destroy();
    break;
  }
}

StreamConsumer 类似于:

public class StreamConsumer extends Thread
{
  private final InputStream _input;

  public StreamConsumer(final InputStream inputStream)
  {
    super();

    _input = inputStream;

    setDaemon(true);
  }


  @Override
  public void run()
  {
    // TODO your code to read the stream
  }
}

就我而言,我不知道这个过程要进行多长时间。所以超时是没有选择的。 我的替代解决方案是启动一个单独的监视线程来监视 IProgressMonitor 的状态。如果进度监视器处于取消状态,则正在执行的线程将被中断。

public static void startMonitorThread(IProgressMonitor monitor) {        
    final Thread runThread = Thread.currentThread();                     
    new Thread("Monitor") {                                              
        @Override                                                        
        public void run() {                                              
            while (runThread.isAlive() && !monitor.isCanceled()) {       
                try {                                                    
                    Thread.sleep(100);                                   
                } catch (InterruptedException exception) {               
                    break;                                               
                }                                                        
            }                                                            
            if (runThread.isAlive()) {                                   
                runThread.interrupt();                                   
            }                                                            
        };                                                               
    }.start();                                                           
}                                                                        

下一个片段展示了一个如何使用它的例子:

startMonitorThread(monitor);                                                                       

final Process process = new ProcessBuilder("...").start();                                         
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
    while (process.isAlive()) {                                                                    
        if (reader.ready()) {                                                                      
            final String line = reader.readLine();                                                 
            // ... do something with line                                                          
        } else {                                                                                   
            Thread.sleep(10);                                                                      
        }                                                                                          
    }                                                                                              
}                                                                                                  

首先,我犯了同样的错误,使用了阻塞的扫描器(并且中断没有起作用)。就像 Ben 的回答提到最好使用 BufferedReader 和非阻塞 ready() 方法来检查是否还有更多内容要阅读。