Android AsyncTask थ्रेड्स सीमाएँ?


95

मैं एक एप्लिकेशन विकसित कर रहा हूं जहां मुझे हर बार उपयोगकर्ता को सिस्टम में लॉग इन करने के लिए कुछ जानकारी अपडेट करने की आवश्यकता होती है, मैं फोन में डेटाबेस का भी उपयोग करता हूं। उन सभी परिचालनों के लिए (अद्यतन, db और आदि से डेटा पुनः प्राप्त करना) मैं async कार्यों का उपयोग करता हूं। अब तक मैंने यह नहीं देखा कि मैं उनका उपयोग क्यों न करूं, लेकिन हाल ही में मैंने अनुभव किया कि अगर मैं अपने कुछ ऑपरेशनों को करता हूं तो कुछ असिंचित कार्य बस पूर्व-निष्पादन पर रुक जाते हैं और doInBackground पर कूदना नहीं पड़ता। यह सिर्फ उस तरह से छोड़ने के लिए बहुत अजीब था, इसलिए मैंने व्हाट्स गलत की जांच करने के लिए एक और सरल एप्लिकेशन विकसित किया। और काफी अजीब है, मुझे एक ही व्यवहार मिलता है जब कुल async कार्यों की गिनती 5 तक पहुंचती है, 6 वीं पूर्व-निष्पादन पर रोकती है।

क्या ऐक्टिविटी / ऐप पर एंड्रॉइड के पास एस्किंट मास्क की एक सीमा है? या यह सिर्फ कुछ बग है और इसे रिपोर्ट किया जाना चाहिए? किसी को भी एक ही समस्या का अनुभव किया और शायद यह एक समाधान पाया?

यहाँ कोड है:

एक पृष्ठभूमि में काम करने के लिए बस उन 5 धागों को बनाएँ:

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");
    }
}

और फिर इस धागे को बनाएं। यह प्री-एक्स्यूट्यूट और हैंग हो जाएगा (यह doInBackground में नहीं जाएगा)।

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");
    }
}

जवाबों:


207

सभी AsyncTasks एक साझा (स्थिर) द्वारा आंतरिक रूप से नियंत्रित कर रहे हैं ThreadPoolExecutor और एक LinkedBlockingQueue । जब आप executeएक AsyncTask पर कॉल करते हैं, तो ThreadPoolExecutorइसे तब निष्पादित करेगा जब यह भविष्य में कुछ समय के लिए तैयार होगा।

'मैं कब तैयार हूं?' a ThreadPoolExecutorका व्यवहार दो मापदंडों, कोर पूल आकार और अधिकतम पूल आकार द्वारा नियंत्रित किया जाता है । यदि वर्तमान में सक्रिय से कम कोर पूल आकार के धागे हैं और एक नया काम आता है, तो निष्पादक एक नया धागा बनाएगा और इसे तुरंत निष्पादित करेगा। यदि कम से कम कोर पूल आकार के थ्रेड चल रहे हैं, तो यह नौकरी को कतार में रखने की कोशिश करेगा और तब तक इंतजार करेगा जब तक कि एक निष्क्रिय धागा उपलब्ध न हो (जब तक कि कोई अन्य काम पूरा न हो जाए)। यदि नौकरी को कतार में रखना संभव नहीं है (कतार में अधिकतम क्षमता हो सकती है), तो यह नौकरियों को चलाने के लिए एक नया थ्रेड (अप-टू-मैक्स पूल आकार के धागे) बनाएगा। गैर-कोर निष्क्रिय थ्रेड्स को अंतिम रूप से डिकम्पोज किया जा सकता है। एक ज़िंदा टाइमआउट पैरामीटर के अनुसार।

Android 1.6 से पहले, कोर पूल का आकार 1 था और अधिकतम पूल का आकार 10. था। Android 1.6 के बाद से, कोर पूल का आकार 5 है, और अधिकतम पूल का आकार 128 है। दोनों मामलों में कतार का आकार 10 है। रख-रखाव का समय 2.3 से पहले 10 सेकंड था, और तब से 1 सेकंड।

इस सब को ध्यान में रखते हुए, अब यह स्पष्ट हो जाता है कि AsyncTaskवसीयत केवल आपके कार्यों के 5/6 क्रियान्वित करने के लिए क्यों दिखाई देगी। 6 वें कार्य को कतारबद्ध किया जा रहा है जब तक कि अन्य कार्यों में से एक पूरा न हो जाए। यह एक बहुत अच्छा कारण है कि आपको लंबे समय तक चलने वाले कार्यों के लिए AsyncTasks का उपयोग नहीं करना चाहिए - यह अन्य AsyncTasks को कभी भी चलने से रोक देगा।

पूर्णता के लिए, यदि आपने 6 से अधिक कार्यों (जैसे 30) के साथ अपने व्यायाम को दोहराया, तो आप देखेंगे कि 6 से अधिक प्रवेश doInBackgroundकरेंगे क्यूंकि कतार पूरी हो जाएगी और अधिक श्रमिक धागे बनाने के लिए निष्पादक को धक्का दिया जाएगा। यदि आपने लंबे समय से चल रहे कार्य के साथ रखा है, तो आपको देखना चाहिए कि 20/30 सक्रिय हो गए, 10 अभी भी कतार में हैं।


2
"यह एक बहुत अच्छा कारण है कि आपको लंबे समय तक चलने वाले संचालन के लिए AsyncTasks का उपयोग नहीं करना चाहिए" इस परिदृश्य के लिए आपकी सिफारिश क्या है? मैन्युअल रूप से एक नया धागा पैदा करना या अपनी खुद की निष्पादक सेवा बनाना?
user123321

2
निष्पादक मूल रूप से थ्रेड्स के शीर्ष पर एक अमूर्त हैं, उन्हें प्रबंधित करने के लिए जटिल कोड लिखने की आवश्यकता को कम करते हैं। यह आपके कार्यों को यह बताता है कि उन्हें कैसे निष्पादित किया जाना चाहिए। यदि आपका कोड केवल एक निष्पादक पर निर्भर करता है, तो यह पारदर्शी रूप से बदलना आसान है कि कितने थ्रेड्स का उपयोग किया जाता है, आदि। मैं वास्तव में थ्रेड बनाने के लिए एक अच्छा कारण के बारे में नहीं सोच सकता, क्योंकि साधारण कार्यों के लिए भी, काम की मात्रा के साथ एक निष्पादनकर्ता समान है, यदि कम नहीं है।
एंटनी

37
कृपया ध्यान दें कि Android 3.0+ से, समवर्ती AsyncTasks की डिफ़ॉल्ट संख्या को घटाकर 1. कर दिया गया है। अधिक जानकारी: developer.android.com/reference/android/os/…
Kieran

वाह, एक महान जवाब के लिए बहुत बहुत धन्यवाद। अंत में मेरे पास एक स्पष्टीकरण है कि क्यों मेरा कोड छिटपुट रूप से और रहस्यमय तरीके से विफल हो रहा है।
क्रिस नाइट

@antonyt, एक और संदेह, रद्द किए गए AsyncTasks, क्या यह AsyncTasks की संख्या की गणना करेगा? यानी, में गिना core pool sizeया maximum pool size?
nmxprime

9

@antonyt के पास सही उत्तर है, लेकिन यदि आप एक सरल समाधान चाहते हैं तो आप सुई की जांच कर सकते हैं।

इसके साथ आप एक कस्टम थ्रेड पूल आकार को परिभाषित कर सकते हैं और, इसके विपरीत AsyncTask, यह सभी एंड्रॉइड संस्करणों पर समान रूप से काम करता है । इसके साथ आप इस तरह की बातें कह सकते हैं:

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);
   }
});

या जैसी चीजें

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

यह और भी बहुत कुछ कर सकता है। GitHub पर इसे देखें ।


अंतिम वचन 5 साल पहले था :(
लुकासटार्स्का

5

अपडेट : चूंकि एपीआई 19 कोर थ्रेड पूल का आकार डिवाइस पर सीपीयू की संख्या को प्रतिबिंबित करने के लिए बदला गया था, न्यूनतम 2 और अधिकतम 4 की शुरुआत में, जबकि अधिकतम सीपीयू * 2 +1 तक बढ़ रहा है - संदर्भ

// 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;

यह भी ध्यान रखें कि जब तक AsyncTask के डिफ़ॉल्ट निष्पादक धारावाहिक (कार्यान्वित एक समय में और वे किस क्रम में आने में एक कार्य), है के साथ विधि

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

आप अपने कार्यों को चलाने के लिए एक निष्पादक प्रदान कर सकते हैं। आप हुड निष्पादक के तहत THREAD_POOL_EXECUTOR प्रदान कर सकते हैं, लेकिन कार्यों के क्रमबद्धता के साथ नहीं, या आप अपना खुद का एक्जिक्यूटर भी बना सकते हैं और यहां प्रदान कर सकते हैं। हालांकि, जावदोस्क में चेतावनी को ध्यान से देखें।

चेतावनी: थ्रेड पूल से समानांतर चलने के लिए कई कार्य करने की अनुमति आम तौर पर किसी को नहीं होती है, क्योंकि उनके संचालन के क्रम को परिभाषित नहीं किया गया है। उदाहरण के लिए, यदि इन कार्यों का उपयोग किसी भी राज्य को सामान्य रूप से संशोधित करने के लिए किया जाता है (जैसे कि बटन क्लिक करने के कारण फ़ाइल लिखना), तो संशोधनों के आदेश की कोई गारंटी नहीं है। सावधानीपूर्वक काम किए बिना डेटा के नए संस्करण के लिए दुर्लभ मामलों में संभव है कि किसी पुराने द्वारा ओवर-राइट किया जाए, जिससे डेटा हानि और स्थिरता के मुद्दों को अस्पष्ट किया जा सके। इस तरह के बदलावों को सीरियल में सबसे अच्छा निष्पादित किया जाता है; इस तरह के काम की गारंटी देने के लिए मंच के संस्करण की परवाह किए बिना क्रमबद्ध किया जाता है आप इस फ़ंक्शन का उपयोग SERIAL_EXECUTOR के साथ कर सकते हैं।

एक और ध्यान देने वाली बात यह है कि दोनों फ्रेमवर्क प्रदान करने वालों ने THREAD_POOL_EXECUTOR और इसके धारावाहिक संस्करण SERIAL_EXECUTOR (जो AsyncTask के लिए डिफ़ॉल्ट है) स्थिर (वर्ग स्तर के निर्माण) हैं और इसलिए आपकी एप्लिकेशन प्रक्रिया में AsyncTask (s) के सभी उदाहरणों में साझा किए गए हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.