简体   繁体   English

如何知道线程是否在SWT环境中完成了任务?

[英]How to know if a Thread has finished its task in SWT environment?

I'm in trouble in getting result from some threads. 我在从某些线程获取结果时遇到麻烦。 I explain the environment, I have a SWT shell with a button. 我解释了环境,我有一个带按钮的SWT shell。 The listener for this button calls a Runnable that inside its run() calls a method that instantiate N threads for performing some operations. 此按钮的侦听器调用Runnable,然后在其run()内部调用实例化N个线程以执行某些操作的方法。 The problem is: how can I display a message dialog or something on the screen, when all the computation is terminated? 问题是:当所有计算终止时,如何在屏幕上显示消息对话框或其他内容? The code I have is something similar 我的代码是类似的东西

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

And inside MyThread class I have 在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
   }
}

So inside the widgetSelected method of the button listener, I want to put something that alerts the user that all threads called by the listener have successfully terminated. 因此,在按钮侦听器的widgetSelected方法内,我想放置一些东西来提醒用户侦听器调用的所有线程都已成功终止。 Is there a way to know that? 有办法知道吗? If I put the awaitTermination statement inside the Listener, the shell becomes not responsive and freezes on the screen. 如果我将awaitTermination语句放在侦听器中,则外壳将无响应,并在屏幕上冻结。 Hope someone could help me. 希望有人可以帮助我。 If something was not clear, please tell me. 如果不清楚,请告诉我。 Thanks all. 谢谢大家

This is "straight forward" - but a bit of work: you "simply" have to enhance your Runnables to somehow signal their progress. 这是“直截了当”的,但需要做一些工作:您“简单地”必须增强您的Runnable,以某种方式表明其进展。

In other words: the ExecutorService interface doesn't offer any means to figure how many of scheduled "tasks" completed. 换句话说:ExecutorService接口不提供任何方法来计算完成的预定“任务”数量。 So - if you need that information, you have to "bake" it into your Runnable. 所以-如果您需要该信息,则必须将其“烘焙”到您的Runnable中。

One idea: create a Map<Runnable, Integer> upfront. 一个想法:预先创建Map<Runnable, Integer> The keys are your Runnable objects, and the value could represent some progress information. 这些键是您的Runnable对象,该值可以表示一些进度信息。 You start with all values at 0. And then you pass that map to each Runnable - and the Runnable simply updates its value at certain, defined points in time. 您从所有值为0的值开始。然后将该映射传递给每个Runnable-Runnable只需在特定的定义时间点更新值。 Maybe ten steps, or maybe just 4. And as soon as all map values are at say 100, you know that you are done! 可能需要10个步骤,也可能只有4个步骤。 所有地图值都等于100时,您就知道完成了! That implies that your main thread simply loops and checks the map content every other second/minute/... and of course: this extra thread should not be the event dispatcher thread. 这意味着,你的主线程只是循环和检查地图内容每隔一秒/分钟/ ...当然:这额外的线程应该是事件调度线程。 You don't want to stall your UI while doing this. 您不想在执行此操作时停止UI。

( of course: you should use a ConcurrentHashMap to implement this ). (当然:您应该使用ConcurrentHashMap来实现这一点)。

Long story short: this information is available already - the code in your Runnable knows what it is doing, right?! 长话短说:这些信息已经可用-Runnable中的代码知道它在做什么,对吗? So you "only" have to make this information accessible to the outer world somehow. 因此,您“仅”必须以某种方式使外部世界可以访问此信息。 There are many options to do that; 有很多选择可以做到这一点。 the above is just one way to get there. 以上只是到达那里的一种方法。

I would recommend taking a look at using FutureCallbacks which are part of the Guava library. 我建议您看一下使用Guava库中一部分的FutureCallbacks

What this will allow you to do is create a ListenableFuture for each task that you fire off. 这将允许您执行的操作是为您触发的每个任务创建一个ListenableFuture In your case, it sounds like this would be represented by a Runnable , but you can just as easily use a Callable . 在您的情况下,听起来这将由Runnable表示,但是您也可以轻松使用Callable When these tasks are all fired off, you will end up with a list of these ListenableFuture objects, which can be "flattened" into a single ListenableFuture which represents the completion of ALL of the tasks. 当这些任务全部解雇后,您将得到这些ListenableFuture对象的列表,可以将这些对象“展平”为一个代表所有任务已完成的ListenableFuture This is accomplished with the method Futures.allAsList(...) : 这是通过Futures.allAsList(...)方法完成的:

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

Now that you have a single ListenableFuture which represents the completion of all of your tasks, you can easily listen for its completion by adding a FutureCallback to be invoked upon completion: 现在,您拥有一个表示所有任务完成情况的单个ListenableFuture ,您可以通过添加在完成时调用的FutureCallback轻松监听其完成情况:

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

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

Now, once these tasks are completed, we need to update the UI to notify users. 现在,完成这些任务后,我们需要更新UI来通知用户。 To do so, we must be sure that the UI updates happen back on the SWT UI thread: 为此,我们必须确保UI更新发生在SWT UI线程上:

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

Note that this can easily be done within the onSuccess() method above so that the result of the tasks can be used. 请注意,可以在上面的onSuccess()方法中轻松完成此操作,以便可以使用任务的结果。

Putting it all together, we can easily loop through a handful of ListeningExecutorService .submit(...) calls for background execution (so as not to block the UI thread - In my example below, you can freely type in the text box while the tasks are running in the background), grab all the ListenableFutures , and add a callback to be invoked upon completion, which will hop back to the UI thread to make the UI updates. 放在一起,我们可以轻松地循环执行几个ListeningExecutorService .submit(...)调用以执行后台操作(以免阻塞UI线程-在下面的示例中,您可以在文本框中自由键入内容,而任务在后台运行),获取所有ListenableFutures ,并添加一个在完成时要调用的回调,这将跳回到UI线程以进行UI更新。


Full example: 完整示例:

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();
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM