2015-09-17 14 views
13

Równoległe działanie jest wykonywane przez podzielenie go na dokładną liczbę dostępnych rdzeni, a następnie uruchomienie tego samego numeru AsyncTask, wykonanie tej samej operacji, ale na różnych częściach danych.Czekaj na wykonanie wielu funkcji AsyncTask

Używam executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ...) w celu zrównoleglenia ich wykonania.

Chciałbym wiedzieć, kiedy każdy wątek kończy swoją pracę, aby połączyć wszystkie wyniki i wykonać dalsze operacje.

Jak mogę to zrobić?

+0

Jeśli chcesz czekać, to wolałbym za pomocą gwintu, jak można po prostu dołączyć do wątku. Cały sens używania Async polega na tym, że jest wykonywany równolegle. Na pewno możesz poczekać na wynik, używając Future dla Async. –

+0

Czy masz kontrolę nad 'AsyncTask's i czy znasz liczbę zadań z góry? jeśli tak, możesz po prostu odliczać (liczbę całkowitą, bez wymyślnej synchronizacji) liczbę zakończonych zadań w 'onPostExecute()' i działać, gdy liczba osiągnie zero. – dhke

+0

@dhke w tym przypadku musisz uruchomić bezczynną pętlę i nie podoba mi się to zbytnio –

Odpowiedz

12

Można też po prostu zmniejszamy licznik w obiekcie udostępnionym w ramach onPostExecute. Ponieważ onPostExecute działa na tym samym wątku (głównym wątku), nie musisz się martwić o synchronizację.

UPDATE 1

Wspólna obiekt może wyglądać następująco:

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

Zgodnie z uwagami do tej odpowiedzi, PO szuka rozwiązania w którym może uniknąć budowania nowej klasy. Można to zrobić poprzez dzielenie AtomicInteger wśród potomnych 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"));  
     } 
    } 
} 
+0

Myślę, że w ten sposób możesz mieć problem z wielokrotnym dostępem i modyfikacją tej samej zmiennej przez wiele wątków, jeśli zakończy się w tym samym czasie .. –

+1

Nie, ponieważ onPostExecute jest uruchamiany w głównym wątku. zmienna jako część doInBackground –

+0

Więc zaproponujesz, aby aktywność czekała w bezczynnej pętli, aż licznik osiągnie zero? To najlepsze rozwiązanie. –

4

Powinieneś użyć CountDownLatch. Oto dokumentacja z przykładami: java.util.concurrent.CountDownLatch

Zasadniczo daje odniesienie CountDownLatch do swoich wątków, a każdy z nich będzie go zmniejszać po zakończeniu:

countDownLatch.countDown(); 

główny wątek będzie czekać na zakończenie wszystkich wątki za pomocą:

countDownLatch.await(); 
+0

Jakie jest jego zachowanie w przypadku równoczesnego dostępu do CountDownLatch? –

+0

Jest bezpieczny dla wątków, ponieważ CountDownLatch jest narzędziem synchronizacji. –

+0

To rozwiązanie jest dla mnie bardzo interesujące, ale problem polega na tym, że możesz go używać tylko wtedy, gdy nie masz parametrów do przekazania do AsyncTask (w moim przypadku przekazuję do mojego zadania kilka liczb całkowitych i nie mogę przekazać CountDownLatch na tym samym poziomie czas i odwołać się do niego w 'onPostExecute' nie działa poprawnie –

Powiązane problemy