2017-07-20 47 views
1

我在从某些线程获取结果时遇到了麻烦。 我解释了环境,我有一个SWT外壳和一个按钮。这个按钮的监听器调用一个Runnable,它的run()调用一个实例化N个线程来执行一些操作的方法。问题是:当所有计算终止时,如何在屏幕上显示消息对话框或其他内容?我的代码是类似的东西如何知道一个线程是否在SWT环境中完成其任务?

public void widgetSelected(SelectionEvent event) { 
    Runnable t = new MyThread(params); 
    executor.execute(t); 
    } 

而且MyThread的类中我有

public void run(){ 
    myMethod(); 
} 

public void myMethod(){ 
    for(int i =0; i<queue.length; i++){ 
     Runnable thread = new AnotherThread(); 
     executor.execute(thread); 
    } 
    executor.shutdown(); 
    if(executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS){ 
    //here I know all thread of kind AnotherThread have finished 
    } 
} 

使按键听众的widgetSelected方法里面,我想把东西,提醒所有线程调用用户由听众成功终止。 有没有办法知道?如果我将LisitTermination语句放在Listener中,那么shell将不会响应并冻结在屏幕上。 希望有人能帮助我。如果事情不清楚,请告诉我。 谢谢大家。

+0

有一个看看这篇文章:https://stackoverflow.com/questions/1250643/how-to-wait-for-all-threads-to-finish -using-executorservice/36718097#36718097 –

回答

1

这是“直接的” - 但一些工作:你“只是”必须提高您的Runnable以某种方式显示自己的进步。

换句话说:ExecutorService接口不提供任何方法来计算完成了多少个计划“任务”。所以 - 如果你需要这些信息,你必须把它“烘烤”到你的Runnable中。

一个想法:先创建一个Map<Runnable, Integer>。键是你的Runnable对象,值可以表示一些进度信息。你开始为0的所有值,然后你传递映射到每个Runnable的 - 和了Runnable只是在特定的定义点及时更新价值。也许十步,或者只是4而一旦所有地图值都在说100,你知道,你做!这意味着你的主线程简单地循环和检查地图内容每隔一秒钟/分钟......当然:这个额外线程应该不是是事件分派器线程。这样做时你不想拖延你的用户界面。

(当然:你应该使用的ConcurrentHashMap来实现这一点)。

长话短说:这些信息已经可用 - 你的Runnable中的代码知道它在做什么,对吧?!所以你“只”必须以某种方式让外部世界获得这些信息。有很多选择可以做到这一点;以上只是到达目的地的一种方式。

+0

这似乎有点过于复杂的方法,并强制主线程不断检查完成不是一个好主意。这会导致UI线程在这些检查期间被阻塞,这会导致与OP询问的相同的无响应。 – avojak

+0

A)谁说*他的*“主”线程是事件分派器线程? B)但采取了一点;我相应地改变了我的答案C)但是接下来:我们正在讨论迭代地图以检查其值。如果这给了足够的流失来冻结UI,那么该系统已经超载了! – GhostCat

+0

在SWT中,[事件从主线程派发](http://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Fguide%2Fswt_threading.htm)。我同意在实践中,你的建议实际上不应该让UI变慢,但如果使用基于事件的设计进行这种忙碌等待的想法让我停下来。 – avojak

1

我建议看一下使用FutureCallbacks这是番石榴图书馆的一部分。

这将允许您执行的操作是为您触发的每项任务创建一个ListenableFuture。根据你的情况,这听起来像这样将由Runnable表示,但你可以很容易地使用一个Callable。当这些任务都发射了,你最终会与这些ListenableFuture对象,它可以被“扁平化”到一个单一的ListenableFuture代表的所有任务完成的列表。这是通过与方法Futures.allAsList(...)

final List<ListenableFuture<T>> futures = ... 
final ListenableFuture<List<T>> combinedFuture = Future.allAsList(futures); 

现在,你有一个ListenableFuture代表所有任务的完成,你可以很容易地通过添加FutureCallback听其完成完成时被调用:

Futures.addCallback(future, new FutureCallback<List<String>>() { 
    @Override 
    public void onFailure(final Throwable arg0) { 
     // ... 
    } 

    @Override 
    public void onSuccess(final List<String> arg0) { 
     // ... 
    } 
} 

现在,一旦这些任务完成,我们需要更新UI以通知用户。要做到这一点,我们必须确保UI更新SWT UI线程上发生回:

Display.getCurrent().asyncExec(new Runnable() { 
    @Override 
    public void run() { 
     // Update the UI 
    } 
}); 

注意,这可以很容易的的onSuccess内进行()上面的方法,这样的任务的结果可能是用过的。我们可以很容易地通过一些ListeningExecutorService .submit(...)调用来执行后台执行(以免阻塞UI线程 - 在我的示例中,您可以自由输入文本框,而任务在后台运行),抓住所有的ListenableFutures,并添加一个回调被调用完成后,这将跳回UI线程,使UI更新。


完整的示例:

import java.util.ArrayList; 
import java.util.List; 
import java.util.concurrent.Callable; 
import java.util.concurrent.Executors; 

import org.eclipse.swt.SWT; 
import org.eclipse.swt.events.SelectionAdapter; 
import org.eclipse.swt.events.SelectionEvent; 
import org.eclipse.swt.layout.FillLayout; 
import org.eclipse.swt.layout.GridData; 
import org.eclipse.swt.layout.GridLayout; 
import org.eclipse.swt.widgets.Button; 
import org.eclipse.swt.widgets.Composite; 
import org.eclipse.swt.widgets.Display; 
import org.eclipse.swt.widgets.Shell; 
import org.eclipse.swt.widgets.Text; 

import com.google.common.util.concurrent.FutureCallback; 
import com.google.common.util.concurrent.Futures; 
import com.google.common.util.concurrent.ListenableFuture; 
import com.google.common.util.concurrent.ListeningExecutorService; 
import com.google.common.util.concurrent.MoreExecutors; 

public class CallbackExample { 

    private final Display display; 
    private final Shell shell; 
    private final Text output; 
    private final ListeningExecutorService executor; 

    public CallbackExample() { 
     display = new Display(); 
     shell = new Shell(display); 
     shell.setLayout(new FillLayout()); 

     executor = MoreExecutors.listeningDecorator(Executors 
       .newFixedThreadPool(20)); 

     final Composite baseComposite = new Composite(shell, SWT.NONE); 
     baseComposite 
       .setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 
     baseComposite.setLayout(new GridLayout()); 

     output = new Text(baseComposite, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL); 
     output.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 

     final Button button = new Button(baseComposite, SWT.PUSH); 
     button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); 
     button.setText("Start tasks"); 
     button.addSelectionListener(new SelectionAdapter() { 

      @SuppressWarnings("synthetic-access") 
      @Override 
      public void widgetSelected(final SelectionEvent e) { 
       // Start tasks when the button is clicked 
       startTasks(); 
      } 

     }); 

    } 

    private void startTasks() { 
     // Create a List to hold the ListenableFutures for the tasks 
     final List<ListenableFuture<String>> futures = new ArrayList<ListenableFuture<String>>(); 
     // Submit all the tasks for execution (in this case 100) 
     for (int i = 0; i < 100; i++) { 
      final ListenableFuture<String> future = executor 
        .submit(new Callable<String>() { 
         @Override 
         public String call() throws Exception { 
          // Do the work! Here we sleep to simulate a long task 
          Thread.sleep(2000); 
          final long currentMillis = System 
            .currentTimeMillis(); 
          System.out.println("Task complete at " 
            + currentMillis); 
          return "Task complete at " + currentMillis; 
         } 
        }); 
      // Add the future for this task to the list 
      futures.add(future); 
     } 
     // Combine all of the futures into a single one that we can wait on 
     final ListenableFuture<List<String>> future = Futures 
       .allAsList(futures); 
     // Add the callback for execution upon completion of ALL tasks 
     Futures.addCallback(future, new FutureCallback<List<String>>() { 

      @Override 
      public void onFailure(final Throwable arg0) { 
       System.out.println("> FAILURE"); 
      } 

      @SuppressWarnings("synthetic-access") 
      @Override 
      public void onSuccess(final List<String> arg0) { 
       System.out.println("> SUCCESS"); 
       // Update the UI on the SWT UI thread 
       display.asyncExec(new Runnable() { 

        @Override 
        public void run() { 
         final StringBuilder sb = new StringBuilder(); 
         for (final String s : arg0) { 
          sb.append(s + "\n"); 
         } 
         final String resultString = sb.toString(); 
         output.setText(resultString); 
        } 

       }); 
      } 

     }); 
    } 

    public void run() { 
     shell.setSize(200, 200); 
     shell.open(); 

     while (!shell.isDisposed()) { 
      if (!display.readAndDispatch()) { 
       display.sleep(); 
      } 
     } 
     executor.shutdownNow(); 
     display.dispose(); 
    } 

    public static void main(final String... args) { 
     new CallbackExample().run(); 
    } 

} 
+0

该解决方案具有将依赖关系引入第三方库的缺点。不是每个人都可以轻松做到 – GhostCat

相关问题