eclipse 中的异步作业调度

来源:互联网 发布:宇宙知乎 编辑:程序博客网 时间:2024/06/16 10:49
导读:

  编程的时候经常会遇到一些长时间的操作,比如读取大量文件并进行解析、从远端服务器读取文件、进行复杂的数据库操作等,如果处理不好的话,会造成程序好像死掉了一样。令人震惊的是,很多程序员对此并不在乎,因为他们知道程序为什么而“死掉了”,并向用户解释说程序在做什么,不用担心,只要等就可以了。如果站在用户的角度思考一下就知道这种想法有多么可怕。

  这里讲作者经历过的事情:曾经开发过一个从超大XML文件(大于10M)中导入数据并插入到数据库中的功能,由于在导入每一条数据的时候都要把和这条数据有关的数据从数据库中取出来,然后进行一定的处理后再插入到数据库中,所以耗时是非常长的,一般都要耗时半个小时以上。在做第一个版本的时候没有考虑进度条,当把程序发给用户的时候,用户用了一会儿就打电话过来:“那个程序死掉了,帮我看看吧!”,通过向他解释这是正常的,他这才将信将疑地放下电话,没过了5分钟,又打电话过来“怎么还是死的,你们怎么做的程序,我要投诉你!”。后来终于导入成功了,但是从用户的反馈来看,他们是十分的不满意。后来在给这个程序开发bug修复补丁的时候顺手给程序加上了进度条的功能,随时报告当前的进度,几乎没有增加工作量。谁知发给客户以后,客户赞扬说:“这个版本改进比较大呀,好多了,不错!现在我都是单击完【导入】按钮以后就去做别的事情了,时不时地回来看看导入进度!”——作者这才深刻的意识到“进度条”这个在技术人员看起来微不足道的小功能在改善用户体验方面有多么重要的作用。

  后来在去客户现场做支持的时候看到的一幕又感到猛然一惊。所做的那个数据导入功能是ERP系统中的一部分,这个ERP系统是一次可以打开多个内部窗口的(类似于Windows中的MDI),用户可以在一个窗口中录单,切换到另一个窗口中制作报表,或者切换到另一个窗口发邮件。看到用户在打开那个数据导入窗口,单击【导入】按钮后就切换到另外一个窗口进行录单操作了。天呀,如果没有提供那个进度条的功能,那么用户单击【导入】按钮以后整个ERP系统就“死掉了”,用户就无法进行任何操作,也就无法做任何工作,难道这半个多小时要他去上网聊QQ、翻纸牌吗?

  在这一点上Eclipse做的无疑是非常好的。当我们新建一个项目的时候,如果项目的初始化时间比较长,Eclipse就会弹出一个带滚动条的窗口,提示用户正在初始化;对于一些耗时非常长的操作,比如从CVS检出代码,Eclipse会弹出一个带有【在后台运行】按钮的进度对话框,如图3.3所示,用户单击【在后台运行】按钮以后,这个对话框就会关闭,这样用户就可以在Eclipse中进行其他的操作了,避免了长时间等待所造成的时间浪费。

  图3.3 进度条

  我们最常接触的就是IProgressMonitor了,在很多方法中都要求传递此接口的实例,比如编辑器的doSave方法就是如下声明的:

  public void doSave(IProgressMonitor monitor)

  通过这个接口就可以操控进度条来显示我们当前的保存进度了。不过IProgressMonitor并不是进度条对话框,它要“依靠”一个进度显示器来把进度显示出来,比如最常见的进度对话框ProgressMonitorDialog。

  部分任务在运行的时候可以由用户选择取消,当用户取消任务的时候,IProgressMonitor的isCanceled方法会返回true,因此我们在任务进行的时候要实时地去调用isCanceled方法,当发现任务被取消的时候要尽快结束任务。

  我们可以使用Java的标准接口Runnable来实现多线程任务运行,不过在Eclipse中又有了新的选择,那就是IRunnableWithProgress,其声明如下:

  public interface IRunnableWithProgress {

  public void run(IProgressMonitor monitor)

  throws InvocationTargetException, InterruptedException;

  }

  这个类的使用和Runnable非常相似,只要把任务放到run方法中就可以了,最重要的是可以调用monitor来对当前进度显示进行控制。下面就是一个完整的进度条演示例子。

  ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell);

  dialog.run(true, true, new IRunnableWithProgress() {

  public void run(IProgressMonitor monitor)

  throws InvocationTargetException, InterruptedException

  {

  final int ticks = 10000;

  monitor.beginTask("开始操作", ticks);

  try

  {

  for(int i = 0; i
  {

  if(monitor.isCanceled())

  throw new InterruptedException();

  monitor.worked(1);

  }

  } finally

  {

  monitor.done();

  }

  }

  });

  调用beginTask方法来完成任务,ticks参数表示此任务有多少工作量,调用worked方法报告自上次报告以来当前完成的任务数量,在循环中不断通过isCanceled方法判断当前任务是否被用户取消。需要注意,要在finally中调用done方法完成任务,否则会出现进度对话框无法正常关闭的情况。

  除了ProgressMonitorDialog外,在Eclipse中还可以通过其他方式显示进度,比如IWorkbenchWindow 通过在工作台窗口的状态行中显示进度来实现此界面,WizardDialog在向导状态行中显示长时间运行的操作。

  除了可以自己构造进度对话框来显示进度之外,我们还可以调用平台的进度服务,而且Eclipse也推荐使用平台的进度服务,这样可以使所有插件都将具有一致的进度表示。平台的进度服务定义为接口IProgressService,我们可以通过PlatformUI.getWorkbench(). getProgressService方法来调用系统的进度服务,例如:

  IProgressService progressService = PlatformUI.getWorkbench()

  .getProgressService();

  progressService.busyCursorWhile(new IRunnableWithProgress() {

  public void run(IProgressMonitor monitor)

  {

  //执行耗时的操作

  }

  });

  在调用Eclipse的方法或者第三方插件的一些方法的时候,有的方法要求传递一个实现了IProgressMonitor的实例进去,如果我们无法传递或者无需传递的时候,最好不要传递null值进去,而是要传递NullProgressMonitor的一个实例进去,此类位于org.eclipse.core.runtime包下,它实现了IProgressMonitor接口,但是所有方法都是给的空实现,传递此类就避免了被调用方法没有进行空指针判断而造成的麻烦。



本文转自

http://book.csdn.net/bookfiles/550/10055018418.shtml
原创粉丝点击