Android AsyncTask iş parçacığı sınırları?


95

Kullanıcı sisteme her giriş yaptığında bazı bilgileri güncellemem gereken bir uygulama geliştiriyorum, ayrıca telefondaki veritabanını da kullanıyorum. Tüm bu işlemler için (güncellemeler, db'den veri alma vb.) Zaman uyumsuz görevler kullanıyorum. Şimdiye kadar neden kullanmamam gerektiğini anlamadım, ancak son zamanlarda bazı işlemler yaparsam bazı eşzamansız görevlerimin önceden yürütmede durduğunu ve doInBackground'a atlamadığını deneyimledim. Bu, onu böyle bırakmak için çok garipti, bu yüzden neyin yanlış olduğunu kontrol etmek için başka bir basit uygulama geliştirdim. Ve yeterince tuhaf, toplam eşzamansız görevlerin sayısı 5'e ulaştığında aynı davranışı görüyorum, 6'ncı ön-yürütme sırasında durur.

Android'in Etkinlik / Uygulamada asyncTasks sınırı var mı? Yoksa bu sadece bir hata mı ve rapor edilmesi mi gerekiyor? Kimse aynı sorunu yaşadı mı ve belki bir çözüm buldu mu?

İşte kod:

Arka planda çalışmak için bu ileti dizilerinden 5 tanesini oluşturmanız yeterlidir:

private class LongAsync extends AsyncTask<String, Void, String>
{
    @Override
    protected void onPreExecute()
    {
        Log.d("TestBug","onPreExecute");
        isRunning = true;
    }

    @Override
    protected String doInBackground(String... params)
    {
        Log.d("TestBug","doInBackground");
        while (isRunning)
        {

        }
        return null;
    }

    @Override
    protected void onPostExecute(String result)
    {
        Log.d("TestBug","onPostExecute");
    }
}

Ve sonra bu konuyu yaratın. PreExecute'a girecek ve askıda kalacaktır (doInBackground'a gitmeyecek).

private class TestBug extends AsyncTask<String, Void, String>
{
    @Override
    protected void onPreExecute()
    {
        Log.d("TestBug","onPreExecute");

        waiting = new ProgressDialog(TestActivity.this);
        waiting.setMessage("Loading data");
        waiting.setIndeterminate(true);
        waiting.setCancelable(true);
        waiting.show();
    }

    @Override
    protected String doInBackground(String... params)
    {
        Log.d("TestBug","doInBackground");
        return null;
    }

    @Override
    protected void onPostExecute(String result)
    {
        waiting.cancel();
        Log.d("TestBug","onPostExecute");
    }
}

Yanıtlar:


207

Tüm AsyncTasks dahili olarak paylaşılan (statik) bir ThreadPoolExecutor ve LinkedBlockingQueue tarafından kontrol edilir . Bir executeAsyncTask'ı çağırdığınızda, ThreadPoolExecutorgelecekte bir süre hazır olduğunda onu çalıştıracaktır.

'Ne zaman hazırım?' a'nın davranışı ThreadPoolExecutoriki parametre tarafından kontrol edilir, çekirdek havuz boyutu ve maksimum havuz boyutu . Halihazırda aktif çekirdek havuz boyutundan daha az iş parçacığı varsa ve yeni bir iş gelirse, yürütücü yeni bir iş parçacığı oluşturacak ve hemen yürütecektir. En azından çekirdek havuz boyutunda iş parçacığı çalışıyorsa, işi kuyruğa almaya ve boş bir iş parçacığı mevcut olana kadar bekler (yani başka bir iş tamamlanana kadar). İşi kuyruğa almak mümkün değilse (kuyruğun maksimum kapasitesi olabilir), işlerin çalışması için yeni bir iş parçacığı (maksimum havuz boyutuna kadar iş parçacıkları) oluşturur. Çekirdek olmayan boştaki iş parçacıkları sonunda hizmet dışı bırakılabilir canlı tutma zaman aşımı parametresine göre.

Android 1.6'dan önce çekirdek havuz boyutu 1 ve maksimum havuz boyutu 10'du. Android 1.6'dan beri çekirdek havuz boyutu 5 ve maksimum havuz boyutu 128'dir. Her iki durumda da sıranın boyutu 10'dur. Canlı tutma zaman aşımı, 2.3'ten 10 saniye önce ve o zamandan beri 1 saniyeydi.

Tüm bunlar göz önünde bulundurularak, neden AsyncTaskgörevlerinizin yalnızca 5 / 6'sını yerine getirecek gibi görüneceği şimdi anlaşılıyor . 6. görev, diğer görevlerden biri tamamlanana kadar sıraya alınır. Bu, uzun süre çalışan işlemler için AsyncTasks kullanmamanız için çok iyi bir nedendir - diğer AsyncTasks'in hiç çalışmasını engelleyecektir.

Tamlık için, alıştırmanızı 6'dan fazla görevle (örneğin 30) tekrarladıysanız, 6'dan fazla görevin gireceğini göreceksiniz, doInBackgroundçünkü sıra dolacak ve uygulayıcı daha fazla çalışan iş parçacığı oluşturmak için zorlanacak. Uzun süren görevi sürdürdüyseniz, 20 / 30'un aktif hale geldiğini ve 10'un hala kuyrukta olduğunu görmelisiniz.


2
"Bu, AsyncTasks'i uzun süreli işlemler için kullanmamanız için çok iyi bir nedendir" Bu senaryo için öneriniz nedir? Manuel olarak yeni bir iş parçacığı mı yoksa kendi yürütme hizmetinizi mi oluşturuyorsunuz?
user123321

2
Yürütücüler temelde iş parçacıklarının üstünde bir soyutlamadır ve onları yönetmek için karmaşık kod yazma ihtiyacını azaltır. Görevlerinizi nasıl yürütülmesi gerektiğinden ayırır. Kodunuz yalnızca bir yürütücüye bağlıysa, o zaman kaç iş parçacığının kullanıldığını şeffaf bir şekilde değiştirmek kolaydır. Bir Cellat, daha az değilse de aynıdır.
antonyt

37
Lütfen Android 3.0+ sürümünden itibaren varsayılan eşzamanlı AsyncTasks sayısının 1'e düşürüldüğünü unutmayın. Daha fazla bilgi için: developer.android.com/reference/android/os/…
Kieran

Harika bir cevap için çok teşekkürler. Sonunda, kodumun neden bu kadar düzensiz ve gizemli bir şekilde başarısız olduğuna dair bir açıklamam var.
Chris Knight

@antonyt, Bir şüphe daha var, İptal Edilmiş AsyncTasks, AsyncTasks sayısına dahil edilecek mi? yani, sayıldı core pool sizeve maximum pool size?
nmxprime

9

@antonyt doğru cevaba sahip, ancak basit bir çözüm arıyorsanız, Needle'ı kontrol edebilirsiniz.

Bununla özel bir iş parçacığı havuzu boyutu tanımlayabilirsiniz ve AsyncTaskbunun aksine , tüm Android sürümlerinde aynı şekilde çalışır. Bununla şöyle şeyler söyleyebilirsiniz:

Needle.onBackgroundThread().withThreadPoolSize(3).execute(new UiRelatedTask<Integer>() {
   @Override
   protected Integer doWork() {
       int result = 1+2;
       return result;
   }

   @Override
   protected void thenDoUiRelatedWork(Integer result) {
       mSomeTextView.setText("result: " + result);
   }
});

veya benzeri şeyler

Needle.onMainThread().execute(new Runnable() {
   @Override
   public void run() {
       // e.g. change one of the views
   }
}); 

Daha da fazlasını yapabilir. GitHub'da kontrol edin .


son taahhüt 5 yıl önceydi :(
LukaszTaraszka

5

Güncelleme : API 19'dan bu yana, çekirdek iş parçacığı havuzu boyutu, cihazdaki CPU sayısını yansıtacak şekilde değiştirildi, başlangıçta minimum 2 ve maksimum 4 olacak şekilde, maksimum CPU * 2 +1 - Referans

// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;

Ayrıca, AsyncTask'ın varsayılan yürütücüsünün seri olmasına rağmen (bir seferde ve geldikleri sırayla bir görevi yürütür), yöntemle

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params)

Görevlerinizi yürütmesi için bir Executor sağlayabilirsiniz. THREAD_POOL_EXECUTOR'a başlık altında yürütücüyü sağlayabilir, ancak görevlerin serileştirilmesi olmadan veya hatta kendi Yürütücünüzü oluşturabilir ve burada sağlayabilirsiniz. Bununla birlikte, Javadocs'taki uyarıya dikkatlice dikkat edin.

Uyarı: Bir iş parçacığı havuzundan birden fazla görevin paralel olarak çalışmasına izin vermek genellikle kişinin istediği şey değildir, çünkü işlemlerinin sırası tanımlanmamıştır. Örneğin, bu görevler ortak herhangi bir durumu değiştirmek için kullanılıyorsa (bir düğmenin tıklanması nedeniyle bir dosya yazmak gibi), değişikliklerin sırası konusunda hiçbir garanti yoktur. Dikkatli çalışma olmadan, nadir durumlarda verilerin daha yeni sürümünün üzerine eski bir sürüm yazılabilir ve bu da belirsiz veri kaybı ve kararlılık sorunlarına yol açar. Bu tür değişiklikler en iyi seri halinde gerçekleştirilir; bu tür çalışmaların platform sürümüne bakılmaksızın serileştirilmesini garanti etmek için bu işlevi SERIAL_EXECUTOR ile kullanabilirsiniz.

Unutulmaması gereken bir şey daha, hem Yürütücüleri sağlayan THREAD_POOL_EXECUTOR çerçevesinin hem de SERIAL_EXECUTOR (AsyncTask için varsayılan olan) seri sürümünün statik (sınıf düzeyinde yapılar) olması ve dolayısıyla uygulama süreciniz boyunca AsyncTask (ler) in tüm örnekleri arasında paylaşılmasıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.