Java 等待多个异步任务完成

Java 等待多个异步任务完成,java,android,multithreading,android-asynctask,Java,Android,Multithreading,Android Asynctask,我将操作并行化,方法是按可用内核的确切数量进行拆分,然后启动相同数量的AsyncTask,在数据的不同部分执行相同的操作 我正在使用executeOnExecutor(AsyncTask.THREAD\u POOL\u EXECUTOR,…)来并行执行它们 我想知道每个线程何时完成其工作,以便合并所有结果并执行进一步的操作 我该怎么办?你应该使用倒计时锁存器。以下是包含示例的文档: 基本上,你给你的线程一个倒计时锁存的引用,每个线程在完成后都会减少它: countDownLatch.count

我将操作并行化,方法是按可用内核的确切数量进行拆分,然后启动相同数量的AsyncTask,在数据的不同部分执行相同的操作

我正在使用
executeOnExecutor(AsyncTask.THREAD\u POOL\u EXECUTOR,…)
来并行执行它们

我想知道每个线程何时完成其工作,以便合并所有结果并执行进一步的操作


我该怎么办?

你应该使用倒计时锁存器。以下是包含示例的文档:

基本上,你给你的线程一个倒计时锁存的引用,每个线程在完成后都会减少它:

countDownLatch.countDown();
主线程将使用以下命令等待所有线程的终止:

countDownLatch.await();

另一种选择是将所有新线程存储在一个数组中

然后,您可以迭代数组并使用线程[i]等待。加入,等待线程完成

参见join()


迭代完成后,所有线程都完成了,您可以使用

,也可以简单地减少共享对象中的计数器,作为
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
s之间共享
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"));    
        }
    }
}
//TODO根据需要更新类型参数。
公共类MyAsyncTask扩展了AsyncTask{
//应在创建异步任务之前创建此实例。
//它的开始计数应等于将生成的异步任务数。
//重要的是,为所有派生的异步任务提供相同的AtomicInteger,以便它们共享相同的工作计数器。
专用最终原子整数工作计数器;
公共MyAsyncTask(AtomicInteger工作计数器){
this.workCounter=工作计数器;
}
//TODO机具DOIN背景
@凌驾
PostExecute上的公共作废(作废结果){
//工作完成后,减小工作计数器。
int tasksLeft=this.workCounter.decrementAndGet();
//如果计数已达到零,则所有异步任务都已完成。
如果(tasksLeft==0){
//通过发送广播使活动知晓。
LocalBroadcastManager mgr=LocalBroadcastManager.getInstance(this.ctx);
经理发送广播(新意图(“所有任务已完成”);
}
}
}

首先,将此类添加到项目中

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;
    }
}
然后:

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();
(新的异步任务(){
@凌驾
受保护的空位背景(空位…空位){
//做点什么。。。
返回null;
}
@凌驾
受保护的void onPostExecute(void避免){
multiTaskHandler.taskComplete();
}
}).execute();
如果要取消所有任务完成后运行的代码,可以使用
multiTaskHandler.cancel()
。例如,如果您有一个错误(不要忘记取消所有其他任务)

*此解决方案不会暂停主线程接收操作员是您的朋友


摆脱AsyncTark比RX慢,并且您无法处理错误

如果您想等待,那么我更喜欢使用线程,因为您可以简单地加入一个线程。使用Async的关键在于它是并行执行的。当然,您可以使用Future for Async等待结果。您是否可以控制
AsyncTask
s,并且您是否提前知道任务的数量?如果是这样,您可以在
onPostExecute()
中倒计时(整数变量,无需复杂的同步)已完成任务的数量,并在计数为零时采取行动。@dhke在这种情况下,您必须运行一个空闲循环,我也不喜欢它much@NicholasAllio这是指我删除的评论吗?如果是:是的,我从您对答案的评论中意识到,您希望得到通知,而不是等待。使用Rx merge Operator或者这样认为,如果多个线程同时完成,您可能会遇到多个访问和修改同一变量的问题。否,因为onPostExecute在主线程上运行。只是不要作为doInBackground的一部分访问变量。那么您会建议让活动在空闲循环中等待,直到计数器达到零?不确定是不是最好的解决方案……不,那绝对不是一个好的解决方案(事实上,由于无响应,系统可能会关闭活动)。作为在共享对象中设置计数器的一部分,您可以检查更新后的值是否已达到0。如果是这样的话,,做一个本地广播,在你的活动中收听。当然这会起作用,但我正在寻找更方便的方法在并发访问CountDownLatch时它的行为是什么?它是线程安全的,因为CountDownLatch是一个同步工具。这个解决方案对我来说非常有趣,但问题是你只能使用它如果没有要传递给AsyncTask的参数(在我的例子中,我向我的任务传递了几个整数,但我不能同时传递倒计时锁存器并在
onPostExecute
中引用它,因为它工作不正常。您可以扩展AsyncTask并创建一个带有额外参数的构造函数,并通过调用'super.onPostExecute()重写onPostExecute。)'然后是'countDownLatch.countDown()'。“主线程将使用:
countDownLatch.await()
”在所有线程终止时等待。实际上,您需要为这个
await
调用指定一个独立的线程,而不是主线程,否则您将无法使用