2011-10-17 14 views
17

Pracuję nad grą społecznościową, której głównym natywnym kodem jest Android NDK. Gra posiada 3 główne pthreads NDK:Przesyłanie zadań do puli wątków daje RejectedExecutionException

  1. gra gwint
  2. nitki komunikacja serwer
  3. główny wątek renderowania (nazywany przez Renderer.onRender)

Poza tym, na Jawie po stronie, używamy AdWhirl, która spawnuje swój własny wątek poprzez własne ScheduledExecutorService, ale zawarliśmy każde wywołanie "zaplanuj", "prześlij", "opublikuj", "uruchom" itp. z blokiem try-catch, aby złapać RejectedExecutionException. Jednak wciąż na wszystkich nowych wersjach nadal występuje strasznie RejectedExecutionException.

Śledzenie stosu w usłudze Android Market pozostawia niewiele więcej wskazówek, a nasz dział kontroli jakości również nie może dokładnie zidentyfikować problemu, ponieważ prawie nie występuje podczas testu (tylko użytkownicy zgłaszali awarię). Wpływa tylko niewielką część naszych użytkowników, ale nadal jest to ponad 7000 wypadków tygodniowo (niewielka część w porównaniu z dużą liczbą zainstalować bazowej)

java.util.concurrent.RejectedExecutionException 
at   java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1876) 
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:774) 
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1295) 
at android.os.AsyncTask.execute(AsyncTask.java:394) 
at c.onProgressUpdate(Unknown Source) 
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:432) 
at android.os.Handler.dispatchMessage(Handler.java:99) 
at android.os.Looper.loop(Looper.java:123) 
at android.app.ActivityThread.main(ActivityThread.java:4632) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:521) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
at dalvik.system.NativeStart.main(Native Method) 

    java.util.concurrent.RejectedExecutionException: pool=128/128, queue=10/10 
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1961) 
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:794) 
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1315) 
at android.os.AsyncTask.execute(AsyncTask.java:394) 
at c.onProgressUpdate(Unknown Source) 
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:432) 
at android.os.Handler.dispatchMessage(Handler.java:99) 
at android.os.Looper.loop(Looper.java:123) 
at android.app.ActivityThread.main(ActivityThread.java:3691) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:507) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:847) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:605) 
at dalvik.system.NativeStart.main(Native Method) 

    java.util.concurrent.RejectedExecutionException 
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1876) 
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:774) 
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1295) 
at android.os.AsyncTask.execute(AsyncTask.java:394) 
at c.onProgressUpdate(Unknown Source) 
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:432) 
at android.os.Handler.dispatchMessage(Handler.java:99) 
at android.os.Looper.loop(Looper.java:123) 
at android.app.ActivityThread.main(ActivityThread.java:4627) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:521) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
at dalvik.system.NativeStart.main(Native Method) 

Odpowiedz

13

Musisz sprawdzić swój kod, to tworzenie daleko za dużo AsyncTask, niż jest dozwolone.

policy jest ustawiony na

private static final int CORE_POOL_SIZE = 1; 
private static final int MAXIMUM_POOL_SIZE = 10; 
private static final int KEEP_ALIVE = 10; 

Uwaga: ten różni się w różnych wersjach Androida

+0

widzę! Dzięki za wskazówkę, czy mimo to zwiększysz rozmiar lub czy jest jakiś limit? W naszym kodzie nie używamy tylu AsyncTask, ale używamy ScheduledExecutorService i innych executorów i określamy większy threadpool. Istnieje sporo tych executorów, czy istnieje jakiś twardy limit dla całkowitej liczby wątków na wszystkich executorach? Jeśli istnieje łączny limit liczby wątków dla całej aplikacji, będę musiał sprawdzić, czy ... w pewnym momencie nasze aplikacje działają z więcej niż 20 wątkami ... – Zennichimaro

+0

Nie ma twardego ograniczenia, jak na Diane , ale najlepszą praktyką jest zachowanie się w latach 10. Możesz ustawić [maksymalny rozmiar puli] (http://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor.html#setMaximumPoolSize (int)) – Reno

+1

Po przepisaniu aplikacji, aby używać mniejszych Runnable i AsyncTask , a teraz działa i nie zauważono już wyjątku RejectedExecutionException. Dzięki Reno i Diane! – Zennichimaro

17

Chociaż należy oczywiście próbować zachować rzeczy jak najbardziej skuteczny, nie ma arbitralnego ograniczenie liczby wątków, na których "pozwolono" działać, wszystko zależy od tego, jak ułożysz swój kod.

Klasa ThreadPoolExecutor to wyjątkowo dobrze udokumentowana i jest tam, gdzie pojawia się problem, który widzisz. I byłoby recommend reading through it, sprawdź

Aby rozpocząć Zgaduję, że jesteś budynku to z Ant i nie używasz tych parametrów na węźle javac:

<javac debug="true" debuglevel="lines,vars,source" /> 

Albo ten obfuscator ci najwyraźniej użyciu są powodem, że to, co normalnie byłoby najważniejszą częścią śladu stosu jest zamiast po prostu wyprowadzanie:

c.onProgressUpdate(Unknown Source) 

ten jest obecny ICS 4.0.4 źródłem ThreadPoolExecutor.AbortPolicy, jak widać to w zasadzie catch-all, że zawsze zgłasza wyjątek:

/** 
* A handler for rejected tasks that throws a 
* {@code RejectedExecutionException}. 
*/ 

public static class AbortPolicy implements RejectedExecutionHandler { 
    /** 
    * Creates an {@code AbortPolicy}. 
    */ 
    public AbortPolicy() { } 

    /** 
    * Always throws RejectedExecutionException. 
    * 
    * @param r the runnable task requested to be executed 
    * @param e the executor attempting to execute this task 
    * @throws RejectedExecutionException always. 
    */ 
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 
     throw new RejectedExecutionException("Task " + r.toString() + 
              " rejected from " + 
              e.toString()); 
    } 
} 

Dodatkowo Znajdziesz defaultHandler zadeklarowane na górze ThreadPoolExecutor:

private static final RejectedExecutionHandler defaultHandler = new AbortPolicy(); 

Więc w końcu, jeśli spojrzeć na domyślnego konstruktora dla ThreadPoolExecutor:

public ThreadPoolExecutor(int corePoolSize, 
          int maximumPoolSize, 
          long keepAliveTime, 
          TimeUnit unit, 
          BlockingQueue<Runnable> workQueue, 
          ThreadFactory threadFactory) { 
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, 
     threadFactory, defaultHandler); 
} 

Zobaczysz, że to uruchamianiu się przy użyciu to AbortPolicy klasa, która jest jej de błąd RejectedExecutionHandler.

ThreadPoolExecutor zawiera również kilka innych RejectedExecutionHandler podklasy, które można ustawić jako domyślny, takie jak:

/** 
* A handler for rejected tasks that silently discards the 
* rejected task. 
*/ 
public static class DiscardPolicy implements RejectedExecutionHandler { 
    /** 
    * Creates a {@code DiscardPolicy}. 
    */ 
    public DiscardPolicy() { } 

    /** 
    * Does nothing, which has the effect of discarding task r. 
    * 
    * @param r the runnable task requested to be executed 
    * @param e the executor attempting to execute this task 
    */ 
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 
    } 
} 

Pozostałe 3 ThreadPoolExecutor konstruktorzy obejmują opcję uchwytu, więc można albo utworzyć instancję go za pomocą inna obsługi, lub tworzyć własne podklasa jesteś podobny do tego:

package com.justinbuser; 

import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.Executors; 
import java.util.concurrent.RejectedExecutionException; 
import java.util.concurrent.RejectedExecutionHandler; 
import java.util.concurrent.ThreadFactory; 
import java.util.concurrent.ThreadPoolExecutor; 
import java.util.concurrent.TimeUnit; 

public class NoThrowThreadPool extends ThreadPoolExecutor { 

    private static final RejectedExecutionHandler defaultHandler = new AdoptPolicy(); 

    public NoThrowThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { 
     super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); 
     setRejectedExecutionHandler(defaultHandler); 
    } 

    public NoThrowThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, 
      ThreadFactory threadFactory) { 
     super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); 
    } 

    public NoThrowThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, 
      RejectedExecutionHandler handler) { 
     super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); 
    } 

    public NoThrowThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, 
      ThreadFactory threadFactory, RejectedExecutionHandler handler) { 
     super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); 
    } 

    public static class AdoptPolicy extends ThreadPoolExecutor.AbortPolicy { 

     @Override 
     public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { 
      new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()).printStackTrace(); 
     } 
    } 
} 
+0

Rozumiem, dziękuję za poradnik! Pracuję i buduję na Eclipse, nie zmodyfikowałem żadnego z parametrów javac tak, chyba że domyślnie debugowanie javac jest ustawione na true, to nie zostanie włączone do mojej kompilacji. Tak, jest to trudny i czasochłonny problem do rozwiązania .. Nie znalazłem żadnego rozwiązania i od tego czasu przejdę do pośrednictwa AdMob Google, aby zastąpić AdWhirl. Pomoże to skutecznie uniknąć problemu Odrzuconego Egzekucji: P Będę spojrzeć na to ponownie, gdy będę mieć czas. Dzięki! – Zennichimaro

+0

@Justin Moja odpowiedź jest nieaktualna, masz rację (głosowanie w górę itd.). To także nie jest forum, zredagowałem hałas, [przeczytaj FAQ] (http://stackoverflow.com/questions/how-to-answer). – Reno

Powiązane problemy