我正在通过将操作分割为可用核心数量的确切部分,然后启动相同数量的AsyncTask,在不同的数据部分上执行相同的操作来并行化我的操作。
我正在使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ...)
来并行执行它们。
我想知道每个线程何时完成其工作,以便组合所有结果并执行进一步的操作。
我该怎么做?
我正在通过将操作分割为可用核心数量的确切部分,然后启动相同数量的AsyncTask,在不同的数据部分上执行相同的操作来并行化我的操作。
我正在使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ...)
来并行执行它们。
我想知道每个线程何时完成其工作,以便组合所有结果并执行进一步的操作。
我该怎么做?
onPostExecute
中的共享对象中递减计数器。由于onPostExecute
在同一线程(即主线程)上运行,因此不必担心同步问题。
更新1
共享对象可能如下所示:public class WorkCounter {
private int runningTasks;
private final Context ctx;
public WorkCounter(int numberOfTasks, Context ctx) {
this.runningTasks = numberOfTasks;
this.ctx = ctx;
}
// Only call this in onPostExecute! (or add synchronized to method declaration)
public void taskFinished() {
if (--runningTasks == 0) {
LocalBroadcastManager mgr = LocalBroadcastManager.getInstance(this.ctx);
mgr.sendBroadcast(new Intent("all_tasks_have_finished"));
}
}
}
更新 2
根据这个回答的评论,OP正在寻找一种避免构建新类的解决方案。这可以通过在生成的 AsyncTask
之间共享一个 AtomicInteger
来实现:
// TODO Update type params according to your needs.
public class MyAsyncTask extends AsyncTask<Void,Void,Void> {
// This instance should be created before creating your async tasks.
// Its start count should be equal to the number of async tasks that you will spawn.
// It is important that the same AtomicInteger is supplied to all the spawned async tasks such that they share the same work counter.
private final AtomicInteger workCounter;
public MyAsyncTask(AtomicInteger workCounter) {
this.workCounter = workCounter;
}
// TODO implement doInBackground
@Override
public void onPostExecute(Void result) {
// Job is done, decrement the work counter.
int tasksLeft = this.workCounter.decrementAndGet();
// If the count has reached zero, all async tasks have finished.
if (tasksLeft == 0) {
// Make activity aware by sending a broadcast.
LocalBroadcastManager mgr = LocalBroadcastManager.getInstance(this.ctx);
mgr.sendBroadcast(new Intent("all_tasks_have_finished"));
}
}
}
您应该使用CountDownLatch。以下是带有示例的文档: java.util.concurrent.CountDownLatch
基本上,您将CountDownLatch的引用分配给您的线程,每个线程在完成时都会将其递减:
countDownLatch.countDown();
主线程将通过以下方式等待所有线程的终止:
countDownLatch.await();
onPostExecute
中引用它不起作用。 - Nicholas AlliocountDownLatch.await()
等待所有线程终止。您实际上需要专门为此await
调用分配一个单独的线程,而不是主线程,否则您将锁定GUI(从而打败了AsyncTask
的目的,即在主线程之外执行短暂的后台作业)。在超越await
调用之后,这个专门用于等待的线程将通过发送广播或发布到与主线程关联的Handler
来向主线程发出完成作业的信号。” - Janus Varmarkenpublic abstract class MultiTaskHandler {
private int mTasksLeft;
private boolean mIsCanceled = false;
public MultiTaskHandler(int numOfTasks) {
mTasksLeft = numOfTasks;
}
protected abstract void onAllTasksCompleted();
public void taskComplete() {
mTasksLeft--;
if (mTasksLeft==0 && !mIsCanceled) {
onAllTasksCompleted();
}
}
public void reset(int numOfTasks) {
mTasksLeft = numOfTasks;
mIsCanceled=false;
}
public void cancel() {
mIsCanceled = true;
}
}
int totalNumOfTasks = 2; //change this to the number of tasks that you are running
final MultiTaskHandler multiTaskHandler = new MultiTaskHandler(totalNumOfTasks) {
@Override
protected void onAllTasksCompleted() {
//put the code that runs when all the tasks are complete here
}
};
然后在每个任务完成后,添加以下语句:multiTaskHandler.taskComplete();
例如:
(new AsyncTask<Void,Void,Void>() {
@Override
protected Void doInBackground(Void... voids) {
// do something...
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
multiTaskHandler.taskComplete();
}
}).execute();
如果您想取消所有任务完成时运行的代码,可以使用 multiTaskHandler.cancel()
。例如 - 如果您有一个错误(不要忘记还要取消所有其他任务)。
* 此解决方案不会暂停主线程!
AsyncTask
并提前知道任务数量?如果是这样,你可以在onPostExecute()
中简单地计数(整数变量,无需复杂的同步)完成的任务数,并在计数达到零时执行操作。 - dhke