Wait for multiple AsyncTask to complete
Solution 1
You could also simply decrement a counter in a shared object as part of onPostExecute
. As onPostExecute
runs on the same thread (the main thread), you won't have to worry about synchronization.
UPDATE 1
The shared object could look something like this:
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"));
}
}
}
UPDATE 2
According to the comments for this answer, OP is looking for a solution in which he can avoid building a new class. This can be done by sharing an AtomicInteger
among the spawned AsyncTask
s:
// 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"));
}
}
}
Solution 2
You should use a CountDownLatch. Here the documentation with examples: java.util.concurrent.CountDownLatch
Basically you give a reference of CountDownLatch to your threads, and each of them will decrement it when finished:
countDownLatch.countDown();
The main thread will wait on the termination of all threads using:
countDownLatch.await();
Solution 3
First, add this class to your project
public 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;
}
}
Then:
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
}
};
Then in each task - when completed, add the line: multiTaskHandler.taskComplete();
Example:
(new AsyncTask<Void,Void,Void>() {
@Override
protected Void doInBackground(Void... voids) {
// do something...
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
multiTaskHandler.taskComplete();
}
}).execute();
You can use multiTaskHandler.cancel()
if you want to cancel the code that runs when all the tasks have completed. For instance - if you have an error (don't forget to also cancel all the other tasks).
* This solution will not pause the main thread!
Nicholas Allio
iOS Developer, Computer engineer, technology lover, and mobile development fanatic.
Updated on June 11, 2022Comments
-
Nicholas Allio almost 2 years
I am parallelizing my operation by splitting it in the exact number of cores available and then, by start the same number of AsyncTask, performing the same operation but on different portions of data.
I am using
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ...)
in order to parallelize the execution of them.I would like to know when every thread finishes its job so that combine all results and perform further operations.
How can I do?
-
Nicholas Allio over 8 yearsI think in this way you may have problem of multiple access and modification of the same variable by multiple threads if they finish at the same time..
-
Janus Varmarken over 8 yearsNo, as onPostExecute is run on the main thread. Just don't access the variable as part of doInBackground.
-
Nicholas Allio over 8 yearsWhat is its behavior in case of concurrent access to the CountDownLatch?
-
Nicholas Allio over 8 yearsSo you will propose to let the activity wait in an idle loop till the counter reaches zero? Not sure is the best solution then..
-
Janus Varmarken over 8 yearsNo, that would definitely not be a good solution (in fact the activity would probably be closed by the system due to unresponsiveness). You could instead, as part of setting the counter in your shared object, check if the updated value has reached 0. If that is the case, do a local broadcast and listen for that inside your activity.
-
Pouriya Zarbafian over 8 yearsIt is thread safe as a CountDownLatch is a synchronization tool.
-
Nicholas Allio over 8 yearsFor sure this will work but I was looking for something more handy
-
Janus Varmarken over 8 yearsWhat do you mean by more handy? Builtin functionality? Added example of the proposed class. It's pretty simply IMO.
-
Nicholas Allio over 8 yearsHandy in a sense that I would avoid to build a new class exclusively for this purpose
-
Janus Varmarken over 8 yearsI see. I have an alternative approach that you may like more (personally I would prefer the one posted). I will update the answer with the different strategy, give me a few minutes.
-
Nicholas Allio over 8 yearsThis solution is very interesting to me but the problem is that you can use it only if you do not have parameters to pass to your AsyncTask (in my case I pass to my task several Integers and I can not pass a CountDownLatch at the same time and refer to it in
onPostExecute
is not working properly -
Pouriya Zarbafian over 8 yearsMaybe you could extend the AsyncTask and create a constructor with an extra parameter, and override the onPostExecute by calling 'super.onPostExecute()' and then 'countDownLatch.countDown()'.
-
Janus Varmarken over 8 yearsAdded alternative solution that uses an AtomicInteger inside your existing AsyncTask implementation to deduce if all jobs have finished.
-
Janus Varmarken over 5 years"The main thread will wait on the termination of all threads using:
countDownLatch.await()
" you'd actually need to dedicate a separate thread other than the main thread for thisawait
call as otherwise you'd be locking up the GUI (and thereby defeating the purpose ofAsyncTask
, i.e., to perform short-lived background jobs off of the main thread). Upon progressing beyond theawait
call, this thread dedicated for waiting would then have to signal back to the main thread that the jobs were completed by sending a broadcast or posting to aHandler
associated with the main thread. -
Duna over 4 yearsThe upper solutions are bad and slow, use merge operator from Rx
-
Janus Varmarken over 4 years@Duna you're arguing that it's slow to decrement an
int
variable in a shared object? This is not a discussion of whether or not to useAsyncTask
as the OP's question was in the context ofAsyncTask
s. -
MurifoX over 4 yearsThis solution is just perfect.
-
Bogdan Android over 3 yearsMatch very good with my personal problem, adapted it to my needs. Thumb up!