如何知道一个线程是否在 SWT 环境中完成了它的任务?
How to know if a Thread has finished its task in SWT environment?
我无法从某些线程中获取结果。
我解释环境,我有一个带按钮的 SWT shell。此按钮的侦听器调用一个 Runnable,它在其 运行() 内部调用一个实例化 N 个线程以执行某些操作的方法。问题是:当所有计算都终止时,如何在屏幕上显示消息对话框或其他内容?我的代码类似
public void widgetSelected(SelectionEvent event) {
Runnable t = new MyThread(params);
executor.execute(t);
}
在 MyThread 里面 class 我有
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 方法中,我想放置一些东西来提醒用户侦听器调用的所有线程都已成功终止。
有办法知道吗?如果我将 awaitTermination 语句放在 Listener 中,shell 将变得无响应并在屏幕上冻结。
希望有人能帮助我。如果有什么不清楚的,请告诉我。
谢谢大家
这是 "straight forward" - 但有点工作:你 "simply" 必须增强你的 Runnables 以某种方式表明他们的进步。
换句话说:ExecutorService 接口不提供任何方法来确定有多少计划"tasks" 已完成。所以 - 如果您需要该信息,您必须 "bake" 将其放入您的 Runnable。
一个想法:预先创建 Map<Runnable, Integer>
。键是您的 Runnable 对象,值可以表示一些进度信息。您从所有值都为 0 开始。然后将该映射传递给每个 Runnable - 并且 Runnable 只是在某些定义的时间点更新 its 值。可能是十步,也可能只是 4 步。一旦 all 地图值达到 100,您就知道自己完成了!这意味着您的主线程只是循环并每隔 second/minute/... 检查地图内容,当然:这个 extra 线程应该 not 是事件调度线程。您不想在执行此操作时拖延 UI。
(当然:你应该使用 ConcurrentHashMap 来实现它)。
长话短说:此信息已经可用 - Runnable 中的代码知道它在做什么,对吧?!所以你 "only" 必须以某种方式让外界可以访问这些信息。有很多选择可以做到这一点;以上只是到达那里的一种方式。
我建议您看看使用 FutureCallbacks,它是 Guava 库的一部分。
这将允许您创建一个 ListenableFuture
for each task that you fire off. In your case, it sounds like this would be represented by a Runnable
, but you can just as easily use a 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. This is accomplished with the method 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();
}
}
我无法从某些线程中获取结果。 我解释环境,我有一个带按钮的 SWT shell。此按钮的侦听器调用一个 Runnable,它在其 运行() 内部调用一个实例化 N 个线程以执行某些操作的方法。问题是:当所有计算都终止时,如何在屏幕上显示消息对话框或其他内容?我的代码类似
public void widgetSelected(SelectionEvent event) {
Runnable t = new MyThread(params);
executor.execute(t);
}
在 MyThread 里面 class 我有
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 方法中,我想放置一些东西来提醒用户侦听器调用的所有线程都已成功终止。 有办法知道吗?如果我将 awaitTermination 语句放在 Listener 中,shell 将变得无响应并在屏幕上冻结。 希望有人能帮助我。如果有什么不清楚的,请告诉我。 谢谢大家
这是 "straight forward" - 但有点工作:你 "simply" 必须增强你的 Runnables 以某种方式表明他们的进步。
换句话说:ExecutorService 接口不提供任何方法来确定有多少计划"tasks" 已完成。所以 - 如果您需要该信息,您必须 "bake" 将其放入您的 Runnable。
一个想法:预先创建 Map<Runnable, Integer>
。键是您的 Runnable 对象,值可以表示一些进度信息。您从所有值都为 0 开始。然后将该映射传递给每个 Runnable - 并且 Runnable 只是在某些定义的时间点更新 its 值。可能是十步,也可能只是 4 步。一旦 all 地图值达到 100,您就知道自己完成了!这意味着您的主线程只是循环并每隔 second/minute/... 检查地图内容,当然:这个 extra 线程应该 not 是事件调度线程。您不想在执行此操作时拖延 UI。
(当然:你应该使用 ConcurrentHashMap 来实现它)。
长话短说:此信息已经可用 - Runnable 中的代码知道它在做什么,对吧?!所以你 "only" 必须以某种方式让外界可以访问这些信息。有很多选择可以做到这一点;以上只是到达那里的一种方式。
我建议您看看使用 FutureCallbacks,它是 Guava 库的一部分。
这将允许您创建一个 ListenableFuture
for each task that you fire off. In your case, it sounds like this would be represented by a Runnable
, but you can just as easily use a 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. This is accomplished with the method 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();
}
}