论坛首页 Java企业应用论坛

RCP中异步线程的处理

浏览 10289 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-01-16  
在RCP中要在非UI线程中执行UI线程的操作,最简单的方式就是display.syncExec或者display.asyncExec,如果UI线程所需的时间较长的话,则应该使用display.asyncExec

在执行异步线程的时候,我们一般应当继承Job或者UIJob类:UIJob是在UI线程中运行的,可以直接访问窗体组件。Job是在非UI线程中运行,如果在里面想访问窗体组件,需要通过Display.asynExec()或者synExec方式来执行。UIJob要尽量的短,不要过多的占用UI线程的时间。

在我现在的系统中,每当UI需要和Server交互的时候,都需要检测
Server是否启动,如果未启动的话,尝试启动三次,三次不成功则抛出Exception,用MessageDialog给用户提示,启动成功以后,还需载入相关信息。这样的异步线程,就只能放到Job中执行。

然而,这样还是远远不够的,试想一下,如果点完一个MenuItem以后,过了很久才会有所反应,这种用户体验岂不是糟糕透了?于是我们还要提供进度条来提高用户体验度,在RCP中,就是如下的代码:

  1. ProgressMonitorDialog progress = new ProgressMonitorDialog(null);  
  2. progress.setCancelable(true);  
  3. try {  
  4.     progress.run(truetruenew IRunnableWithProgress() {  
  5.         public void run(IProgressMonitor monitor)  
  6.                 throws InvocationTargetException {  
  7.             doSomething();  
  8.         }  
  9.     });  
  10. catch (InvocationTargetException e) {  
  11.     e.printStackTrace();  
  12. catch (InterruptedException e) {  
  13.   
  14. }  

第二行中的
progress.setCancelable(true)是为了允许客户在长时间执行后台进程的时候,可以取消掉进程的执行。这里我把实际的执行过程Extract了出来,免得方法太长,doSomething()看起来应该是这样子的:

  1. public void doSomething(IProgressMonitor monitor) {  
  2.         monitor.beginTask("Beginning...", IProgressMonitor.UNKNOWN);
  3.         monitor.subTask("Doing first job"); 
  4.         doFirstThing();
  5.         if(monitor.isCanceled()) return;
  6.         monitor.worked(1); 
  7.         monitor.subTask("Doing second job");
  8.         doSecondThing();
  9.         if(monitor.isCanceled()) return;
  10.         monitor.worked(2);
  11.         ...... 
  12.         monitor.done();  
  13.     }   
  14. }  

这样子当用户点击ProgressMonitor的Cancel按钮时,
monitor.isCanceled()就会返回true,doSomething便中止执行。

说到这里,可能就会有疑问了,monitor.isCanceled()方法只会在doFirstThing()和doSecondThing()之间才会被触发,那么如果doFirstThing()的过程中有异常情况而导致无法返回,那点击Cancel就根本没有作用啊?

唔......这个就是问题的核心所在了,我不知道别人是如何解决的,在这里我只说一下我的解决方案,希望能够起到抛砖引玉的作用:

在前面已经提到,我使用了Job来处理异步线程,然而Job是通过调用job.schedule来执行的,用户无法确保job被执行的时机以及何时结束,一般的方法是使用Listener,Observer或者某个信号量来指示Job的结束。在有些地方我用的是Observer模式,而在这种情况下,我用的是boolean变量来做指示。于是,上面的doSomething就变成了:

  1. public void doSomething(IProgressMonitor monitor) {  
  2.         monitor.beginTask("Beginning...", IProgressMonitor.UNKNOWN);
  3.         monitor.subTask("Doing first job"); 
  4.         job.schedule();
  5.         while (!jobFinished) {
  6.            if (monitor.isCanceled()) {
  7.               logger.info("monitor is canceled");
  8.               job.cancel();
  9.               return;
  10.            }
  11.            monitor.worked(times);
  12.            times++;
  13.         }
  14.         monitor.done();  
  15.     }   


这样,只要while循环没有满足结束条件,我们就可以通过点击Cancel按钮来cancel掉Job。如果job中还有些东西是无法自动cancel掉的话,比如Socket通信等,我们还可以在job.cancel()前面加上一些代码来做这样的工作,比如job.getSocket().close()等,当然还要处理好各种Exception。

在Eclipse的Article里面,有几篇分别讲述Job和ProgressMonitor的文章,很是详细,有兴趣的朋友不妨找来看看。不过文章只是讲解原理性的东西,开发中所碰到的问题,还是要靠个人的经验来分析解决......所以,还是多多coding,多多thinking,提高解决实际问题的能力吧:)
   发表时间:2007-01-17  
ProgressMonitor的Article:

http://www.eclipse.org/articles/Article-Progress-Monitors/article.html

0 请登录后投票
   发表时间:2007-01-24  
引用
在RCP中要在非UI线程中执行UI线程的操作

没看明白。

RCP里面的异步执行display.asyncExec,实际上还是在UI线程中执行,只是采用了队列的形式来调度。
非UI线程是不允许修改界面元素的。我想这可能是基于同步考虑的简单实现。
0 请登录后投票
   发表时间:2007-01-25  
在非UI的线程里面,如果想改变UI的组件,那么就要通过获得display,然后调用display.asyncExec()来处理
0 请登录后投票
   发表时间:2007-02-11  
doFirstThing()方法只要抛出异常不就可以实现异常向上传递了 难道这样不行?
0 请登录后投票
   发表时间:2007-02-13  
doFirstThing的执行过程,是无法被monitor.cancel终止的
0 请登录后投票
   发表时间:2007-04-28  
设置为IProgressMonitor.UNKNOWN时
进度条只显示出来,并不前进
看了JFace代码好像JFace把IProgressMonitor.UNKNOWN的情况抛弃了
有遇到类似问题的么
0 请登录后投票
   发表时间:2007-04-29  
嗯,你调用monitor.work(int n),就能前进了
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics