1

我一直在使用 ExecutorCompletionService 中的示例代码,并将以下示例代码放在一起。solve() 中的代码按预期工作并打印 solve2() 中的代码不打印任何内容,实际上永远不会退出。ecs 是在将作业提交到 ExecutionService 之前还是之后构造的,都没有关系。
1
2
3
4
5

有没有办法将 CompletionService 构造与 FutureTasks 一起使用?我已经重写了我的生产代码以直接获取() FutureTask 的结果,而不是尝试从 ExecutorCompletionService 获取()它们,但它(当前)导致了一些看起来很乱的东西。简而言之,下面的solve2有什么问题?谢谢。

import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class sample {
public static class stringCallable implements Callable<String>{
    String mstring;

    stringCallable(String s) {mstring = s;}
    @Override
    public String call() throws Exception {
        // TODO Auto-generated method stub
        return mstring;
    }
};

public static void main(String[] args) {
    // TODO Auto-generated method stub
    ArrayList<Callable<String>> list = new ArrayList<Callable<String>>();
    ExecutorService es = Executors.newFixedThreadPool(1);
    Executor e = Executors.newSingleThreadExecutor();
    list.add(new stringCallable("1"));
    list.add(new stringCallable("2"));
    list.add(new stringCallable("3"));
    list.add(new stringCallable("4"));
    list.add(new stringCallable("5"));

    try {
        solve(e, list);
    } catch (InterruptedException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    } catch (ExecutionException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    System.out.println ("Starting Solver 2");

    try {
        solve2(es, list);
    } catch (InterruptedException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    } catch (ExecutionException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
}

static void solve(Executor e, Collection<Callable<String>> solvers)throws InterruptedException, ExecutionException {
    CompletionService<String> ecs = new ExecutorCompletionService<String>(e);
    for (Callable<String> s : solvers)
     ecs.submit(s);
    int n = solvers.size();
    for (int i = 0; i < n; ++i) {
        String r = ecs.take().get();
        if (r != null)
            use(r);
    }
}

static void solve2(ExecutorService e, Collection<Callable<String>> solvers)throws InterruptedException, ExecutionException {
    for (Callable<String> s : solvers){
        FutureTask<String> f = new FutureTask<String>(s);
          e.submit(f);
    }
    CompletionService<String> ecs = new ExecutorCompletionService<String>(e);
    int n = solvers.size();
    for (int i = 0; i < n; ++i) {
        String r = ecs.take().get();
        if (r != null)
            use(r);
    }
}

private static void use(String r) {
    System.out.println (r);
}

}

4

2 回答 2

3

In solve2, when you create a ExecutorCompletionService using the existing ExecutorService, it's submitted tasks are ignored by the wrapper because it uses a separate LinkedBlockingQueue. The submitted tasks are not inherited. So, your code blocks when you do ecs.take().get(); because the ExecutorCompletionService doesn't have, itself, any submitted tasks.

Also, you don't need to specifically create FutureTask's to submit to the ExecutorCompletionService. These Future tasks already are created for you, internally. That's why you get a Future<String> when calling ecs.take();.

Given this, your solve2 function is completely useless. You are already doing it correctly in solve1.

于 2009-12-11T15:27:05.893 回答
0

这就是我将如何实现它:

static void solve2(ExecutorService e, Collection<Callable<String>> solvers)throws InterruptedException, ExecutionException {
    CompletionService<String> ecs = new ExecutorCompletionService<String>(e);
    for (Callable<String> s : solvers){
        ecs.submit(s);
    }
    int n = solvers.size();
    for (int i = 0; i < n; ++i) {
        String r = ecs.take().get();
        if (r != null)
            use(r);
    }
}

The ExecutorCompletionService is "just" a wrapper around ExecutorService, but you must submit your callables to the ECS, as the ECS will take the result of the callable, place it onto a queue. This result is then available via a take() or poll(). If you submit a callable directly on the ExecutorService, the ECS cannot know about its completion. If you look in the javadoc of ECS, it says exactly the same things + good example (even better explanation). I advice you also to take a look at the source code java.util.concurrent.ExecutorCompletionService

于 2009-12-11T15:18:03.710 回答