如何从工作线程到管理线程创建异步消息。框架如下。在代码片段中,Worker实现了Runnable,而threadManager是ExecutorService。 Worker线程长时间运行,并应将定期进度消息传递给管理器。一种选择是使用阻塞队列,但我之前没有这样做过。
RunnableTester_rev2.threadManager = Executors.newFixedThreadPool( nThreadsAllowed );
final List<Worker> my_tasks = new ArrayList<Worker>();
for ( int i = 0; i < nThreadsToRun; i++ )
{
// The Worker thread is created here.
// Could pass a blocking queue to handle messages.
my_tasks.add( new Worker( ) );
}
final List<Future<?>> fList = new ArrayList<Future<?>>();
for ( final Worker task : my_tasks )
{
fList.add( RunnableTester_rev2.threadManager.submit( task ) );
}
RunnableTester_rev2.threadManager.shutdown();
/**
* Are we all done
*/
boolean isDone = false;
/**
* Number of threads finished
*/
int nThreadsFinished = 0;
/**
* Keep processing until all done
*/
while ( !isDone )
{
/*
* Are all threads completed?
*/
isDone = true;
int ii = 0;
nThreadsFinished = 0;
for ( final Future<?> k : fList )
{
if ( !isThreadDoneList.get( ii ) )
{
final boolean isThreadDone = k.isDone();
if ( !isThreadDone )
{
// Reduce printout by removing the following line
// System.out.printf( "%s is not done\n", mywks.get( ii ).getName() );
isDone = false;
}
}
else
{
nThreadsFinished++;
}
ii++;
}
/*
* WOULD LIKE TO PROCESS Worker THREAD MESSAGES HERE
*/
}
答案 0 :(得分:1)
这真的很复杂。我会做这样的事情。
// no need to use a field.
ExecutorService threadManager = Executors.newFixedThreadPool( nThreadsAllowed );
List<Future<Result>> futures = new ArrayList<Future<Result>>();
for ( int i = 0; i < nThreadsToRun; i++ )
// Worker implements Callable<Result>
futures.add(threadManager.submit(new Worker( )));
threadManager.shutdown();
threadManager.awaitTermination(1, TimeUnit.MINUTE);
for(Future<Result> future: futures) {
Result result = future.get();
// do something with the result
}
答案 1 :(得分:0)
我使用BlockingQueue
。你可以尝试这样的事情:
final BlockingQueue<Message> q = new LinkedBlockingQueue<Message>();
// start all the worker threads,
// making them report progress by adding Messages into the queue
for (...) {
threadManager.submit(new Runnable() {
// initialize the worker
q.offer(new Message("10% completed"));
// do half of the work
q.offer(new Message("50% completed"));
// do the rest of the work
q.offer(new Message("100% completed"));
});
}
// after starting all the workers, call a blocking retrieval method
// on the queue, waiting for the messages
while (!(everything is done)) {
Message m = q.take();
// process the message
}
这个想法是所有工作线程和主线程共享一个公共阻塞队列。工作线程是生产者,主线程是消费者。
我在这里使用.offer(...)
,但你可以使用.add(...)
,在这种情况下你会得到相同的结果。不同之处在于,如果队列已满,前者将返回false,而后者将抛出异常。它不可能在这里发生,因为我已经实例化了一个无界队列(显然你可能得到一个OutOfMemoryError)。但是,如果使用.put(...)
,则必须处理未经检查的InterruptedException
(在这种情况下无效:它不能被抛出,因为队列永远不会满 - 它是无界 - 所以该方法永远不会阻止。
显然,您可以通过将对q.offer(...)
的调用重构为reportStatus(...)
方法或类似方法来改进它。
也许你想要的东西与我上面提出的不同。也许你希望主线程只在每个工作线程完成其工作时才能获得异步消息。
我的解决方案类似于Peter Lawrey提出的解决方案,不同之处在于,使用此解决方案,管理线程将在任何工作线程完成后立即得到通知(与Peter的解决方案一起,工作线程的结果将按顺序检索 - 因此,如果第一个工作线程需要永远运行,即使所有其他工作线程完成其工作,您也不会知道它。根据您的工作情况,您可以获得更高的性能。
请注意,使用此解决方案,您无法推断哪个线程已完成其工作(为此,您的Worker
类必须具有类似.getJobId()
的方法或类似的方法),而使用Peter的解决方案你将确切地知道哪个线程完成了它的工作(因为你按照你实例化的同一顺序在futures
列表中进行迭代并启动了Workers)。
如果是这种情况,您可以使用ExecutorCompletionService
。它封装了一个ExecutorService
并提供了一个.take()
方法(阻塞检索),它将在执行者服务完成后立即返回Future
:
ExecutorCompletionService<Worker> ecs = new ExecutorCompletionService(executorService);
// Worker has to be a callable
// Now you submit your tasks to the ecs:
for (...) {
ecs.submit(new Worker(...));
}
// Now you block on .take():
while (!(everything is done)) {
Future<Worker> f = ecs.take();
// f.get(), for example, to check the results
}
CompletionService
保证,一旦你使用Future
,对其.get()
方法的调用将立即完成:通过返回结果,或者通过抛出ExecutionException,封装Callable
抛出的异常。