2012-01-06 9 views
6

मेरे आवेदन में मेरे पास कुछ मूल कोड पर एक रैपर है, जिसे जेएनआई पुल के माध्यम से बुलाया जाता है। इस देशी कोड को अलग थ्रेड (समांतर प्रसंस्करण) में निष्पादित करने की आवश्यकता है। हालांकि समस्या यह है कि कोड कभी-कभी "लटकता" है, इसलिए थ्रेड को "बलपूर्वक" समाप्त किया जाना चाहिए। दुर्भाग्यवश मुझे ऐसा करने के लिए कोई "नाज़ुक" विधि नहीं मिली है: सामान्य सलाह है कि कोड को थ्रेड में कोड को सुन्दर तरीके से बाहर निकलने के लिए बताना है, लेकिन मैं इसे इस मूल कोड (जो ऊपर तीसरा पक्ष कोड है) के साथ नहीं कर सकता।एक धागा को समाप्त करें जो मूल कोड चला रहा है

मैं काम प्रस्तुत करने के लिए जावा समवर्ती एपीआई का उपयोग:

Future<Integer> processFuture = taskExecutor.submit(callable); 

try { 
    result = processFuture.get(this.executionTimeout, TimeUnit.SECONDS).intValue(); 
} 
catch (TimeoutException e) { 
    // How to kill the thread here? 
    throw new ExecutionTimeoutException("Execution timed out (max " + this.executionTimeout/60 + "min)"); 
} 
catch (...) { 
    ... exception handling for other cases 
} 

Future#cancel() केवल धागा को बाधित करता है, लेकिन यह इसे समाप्त नहीं होंगे।

class DestroyableCallable implements Callable<Integer> { 

    private Thread workerThread; 

    @Override 
    public Integer call() { 
     workerThread = Thread.currentThread(); 

     return Integer.valueOf(JniBridge.process(...)); 
    } 

    public void stopWorkerThread() { 
     if (workerThread != null) { 
      workerThread.stop(); 
     } 
    } 
} 

DestroyableCallable callable = new DestroyableCallable(); 

Future<Integer> processFuture = taskExecutor.submit(callable); 

try { 
    result = processFuture.get(this.executionTimeout, TimeUnit.SECONDS).intValue(); 
} 
catch (TimeoutException e) { 
    processFuture.cancel(true); 
    // Dirty: 
    callable.stopWorkerThread(); 

    ThreadPoolTaskExecutor threadPoolTaskExecutor = (ThreadPoolTaskExecutor) taskExecutor; 

    logger.debug("poolSize: " + threadPoolTaskExecutor.getPoolSize() + ", maxPoolSize:" 
        + threadPoolTaskExecutor.getMaxPoolSize() + ", activeCount:" 
        + threadPoolTaskExecutor.getActiveCount()); 
    } 

    throw new ...; 
} 
catch (...) { 
    ... exception handling for other cases 
} 

प्रश्न/इस कोड के साथ समस्याओं:

  • यह सामान्य रूप में ऐसा करने का अधिकार रास्ता नहीं है तो मैं निम्नलिखित चाल का इस्तेमाल किया? कोई और अधिक सुरुचिपूर्ण विकल्प?
  • activeCount कार्य निष्पादक पर कम नहीं है, इसलिए कार्य निष्पादक अभी भी "सोचता है कि" कि धागा चल रहा है
  • मैं stopWorkerThread() विधि के लिए workerThread != null जांच जोड़ने के लिए किया था, के रूप में इस चर निकला null कुछ मामले पर किया जाना है। मुझे समझ में नहीं कर सकते इन मामलों रहे हैं क्या ...

नोट्स:

  • मूल निवासी कोड फ़ाइल वर्णनकर्ता (सॉकेट) का उपभोग नहीं करता है। सबकुछ इसे डेटा के ब्लॉक के रूप में पास कर दिया जाता है और उसी तरह लौटा दिया जाता है।
  • मूल कोड सीपीयू-गहन है। हालांकि यह समाप्त होने की गारंटी देता है, लेकिन इसमें काफी समय लग सकता है।

बाउंटी संपादित: दृष्टिकोण/सुझाव फिर से मूल कोड स्पष्ट है, अपने उत्तर में यह प्रस्ताव नहीं है। मुझे शुद्ध-जावा समाधान/वर्कअराउंड चाहिए।

उत्तर

9

जावा में बल धागा समाप्ति के लिए शुद्ध विकल्प हैं। यह प्राचीन और बहिष्कृत थ्रेड.स्टॉप() केवल (AFAIK) है। और सुरक्षित थ्रेड समाप्ति के लिए विकल्प (यही कारण है कि .stop() को बहिष्कृत किया गया था, और JVM कार्यान्वयनकर्ताओं द्वारा भी लागू नहीं किया जा सकता था)। हां, अगर आप कुछ मनमाना बिंदु में धागे की समाप्ति के लिए मजबूर है, तो आप यकीन है कि क्या धागा समाप्त के लिए साबित नहीं कर सकते कुछ साझा स्मृति छोड़ नहीं करता है/-

कारण एप्लिकेशन के अंदर सभी धागे शेयरों स्मृति और संसाधनों क्या है असंगत राज्य में संसाधन। और आप भी (सामान्य रूप से) मान सकते हैं कि संसाधन (संभवतः) गंदे हैं ('क्या आप बिल्कुल नहीं जानते कि किस बिंदु पर धागा रोक दिया गया था)।

तो, यदि आप अपने ऐप के कुछ धागे को बाधित करने में सक्षम होना चाहते हैं, तो एकमात्र समाधान प्रदान करना है - डिज़ाइन चरण पर - "savepoints" के कुछ नोटेशन - लक्षित थ्रेड के कोड में स्थान, जो गारंटीकृत हैं साझा स्थिति को म्यूटेट नहीं करने के लिए, इसलिए थ्रेड के लिए यहां से बाहर निकलना सुरक्षित है। और यह वही है जो Thread.stop() javadocs आपको बता रहे हैं: धागे को सुरक्षित रूप से बाधित करने का एकमात्र तरीका थ्रेड के कोड को डिज़ाइन करना है ताकि यह किसी प्रकार के इंटरप्ट अनुरोध के जवाब में स्वयं ही प्रतिक्रिया दे सके। कुछ प्रकार का ध्वज, जिसे समय-समय पर थ्रेड द्वारा चेक किया जाता है।

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

तो आप छोटे अलग एप्लिकेशन को डिजाइन करते हैं (जावा में भी हो सकते हैं - यदि आपकी तीसरी पार्टी लिब अन्य बाइंडिंग प्रदान नहीं करती है, या यहां तक ​​कि शेल-स्क्रिप्ट में भी) जो केवल आपको गणना करने के लिए नौकरी है। आप मुख्य ऐप से ऐसी प्रक्रिया शुरू करते हैं, इसे नौकरी देते हैं, और वॉचडॉग शुरू करते हैं। यह वॉचडॉग टाइमआउट का पता लगाता है - यह जबरन प्रक्रिया को मारता है।

यह समाधान का एकमात्र मसौदा है। यदि आप प्रदर्शन में सुधार करना चाहते हैं (प्रक्रिया शुरू करने में समय लग सकता है), और ...

+0

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

+1

कोई ताले नहीं - ठीक है, हो सकता है। लेकिन देशी कोड में मेमोरी लीक के बारे में क्या? क्या आप सुनिश्चित कर सकते हैं कि देशी lib को असामान्य समाप्ति प्रक्रिया को गहन तरीके से संसाधित करने के लिए पर्याप्त सुरक्षित और कोडित किया गया था? क्या यह असामान्य एक सहित सभी निकास पथों पर आवंटित स्मृति जारी करता है? मूल कोड में लीक, स्मृति के साथ आप क्या करेंगे?कुछ ओएस-स्तरीय संसाधनों के बारे में वही सवाल - क्या होगा यदि मूल lib कर्नेल-स्तर लॉक आवंटित करता है? कई अतिरिक्त धागे शुरू करता है? – BegemoT

+0

मैं ढेर पर मेमोरी लीक से संबंधित हूं (जैसा कि [धागे एक ही ढेर साझा करते हैं] (http://stackoverflow.com/questions/1665419))। मैं एक विशेषज्ञ नहीं हूं, लेकिन मुझे नहीं लगता कि उपयोगकर्ता थ्रेड को कर्नेल मोड में बाधित किया जा सकता है, अन्यथा धागे को मारने से नुकसान होगा। डिस्क पर कुछ लिखते समय थ्रेड मारा गया था, तो फाइल सिस्टम। तो अप्रकाशित कर्नेल-स्तर ताले बहुत दुखी होंगे। बाकी के लिए मैं आपकी स्थिति का पूर्ण समर्थन करता हूं: प्रक्रिया अधिक सुरक्षित है। जेएनआई पुल पर अधिक स्टैंडअलोन प्रक्रिया से मेरे द्वारा बनाया गया था, इसलिए इसे वापस करने का कोई प्रयास नहीं है। –

1

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

http://java.sun.com/docs/books/jni/html/other.html

+0

यदि मैं सही ढंग से समझ गया, तो आपका मतलब है कि जावा थ्रेडिंग इस तरह के मामले को संभालने के लिए आत्मसमर्पण करता है? –

1

निश्चित रूप से एक बदसूरत हैक तुम यहाँ है ...

सबसे पहले, थ्रेड पूल धागे को व्यक्तिगत रूप से शांत होना चाहिए नहीं कर रहे हैं और आम तौर पर पूरा होने तक चलाने के लिए, विशेष रूप से नहीं छोड़ा जाना चाहिए Thread.stop() के साथ बंद किया गया है जो सामान्य धागे के लिए भी अनुशंसित नहीं है।

जैसा कि मैंने कहा है, Thread.stop() का उपयोग कभी भी प्रोत्साहित नहीं किया जाता है और आम तौर पर थ्रेड को असंगत स्थिति में छोड़ देता है, जो थ्रेड पूल के कारण थ्रेड को "मृत" के रूप में नहीं देखता है। यह शायद इसे भी मार नहीं सकता है।

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

संपादित: बिंदु 3 के रूप में, यह संभव है कि आप volatile के रूप में वर्तमान धागा के संदर्भ में घोषित करने के लिए जब से तुम एक थ्रेड में बिना निर्दिष्ट दूसरे में उसका पढ़ रहे हैं की जरूरत है:

private volatile Thread workerThread; 

संपादित करें: मुझे यह विचार मिलना शुरू हो रहा है कि आपका जेएनआई कोड केवल संख्यात्मक गणना करता है और किसी असंगत स्थिति में रहने वाले किसी भी हैंडल को नहीं खोलता है, तो धागे को अचानक मार दिया जाना चाहिए। क्या आप इसकी पुष्टि कर सकते हैं?

उस स्थिति में, मुझे अपनी सलाह के खिलाफ जाने दें और आपको बताएं कि इस मामले में आप Thread.stop() के साथ थ्रेड को सुरक्षित रूप से मार सकते हैं। हालांकि, मैं आपको एक असंगत स्थिति में थ्रेड पूल छोड़ने से बचने के लिए थ्रेड पूल थ्रेड के बजाय एक अलग थ्रेड का उपयोग करने की सलाह देता हूं (जैसा आपने बताया है, यह थ्रेड को मृत के रूप में नहीं देखता है)। यह और भी व्यावहारिक है, क्योंकि आपको धागे को खुद को रोकने के लिए उन सभी चालों को करने की ज़रूरत नहीं है, क्योंकि आप थ्रेड पूल धागे के विपरीत, सीधे मुख्य थ्रेड से stop() पर कॉल कर सकते हैं।

+0

** ट्यूडर **, तीसरे बिंदु को इंगित करने के लिए धन्यवाद, इसके लिए +1। हां, मुझे लगता है कि देशी कोड "लटका" क्यों है: डेटा की प्रसंस्करण बहुत अधिक समय लेती है, और इस मामले में मैं डेटा को छोड़ना चाहता हूं (यह मेरे मामले में प्रसंस्करण गति के लिए कुछ डेटा खोना ठीक है)। आप मूल कोड की समीक्षा करना चाहते हैं। * और * के अलावा कुछ और? –

+0

@dma_k: क्या आपके पास कोई गारंटी है कि मूल कोड अंततः समाप्त हो जाएगा? – Tudor

+0

हां, मुझे गारंटी है। लेकिन इसमें दिन लग सकते हैं :) –

0

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

HTH जेरोम

+0

क्या यह ब्रोकर जावा में लिखा जाना चाहिए? यदि हां, तो क्या आप इसके लिए मेटा-लॉजिक प्रदान कर सकते हैं? आपने जो कहा है, उससे ब्रोकर को घटनाओं को संभालने के लिए अभी तक एक और अलग थ्रेड में भागना चाहिए ... –

2

आप एक अलग जावा अनुप्रयोग में JNI विधि को यह एक फोन लपेट सकते हैं और फिर java.lang.Process के साथ एक और जावा प्रक्रिया बांट। फिर आप ओएस स्तर पर उस प्रक्रिया को नष्ट करने के लिए Process.destroy() पर कॉल कर सकते हैं।

आपका पर्यावरण क्या है और अन्य विचारों के आधार पर आपको जावा निष्पादन योग्य तरीके से पता लगाने के लिए कुछ युक्तियां करनी पड़ सकती हैं, खासकर यदि आप कुछ पुनर्वितरण योग्य सॉफ्टवेयर बना रहे हैं जो विभिन्न प्लेटफार्मों पर चल सकते हैं। एक और मुद्दा आपके लिए आईपीसी होगा, लेकिन यह प्रोसेस के इनपुट/आउटपुट स्ट्रीम का उपयोग करके किया जा सकता है।

+0

जबरन एक प्रक्रिया को मारना धागे को रोकने से भी कम सुरुचिपूर्ण है। – Tudor

+3

जबरन एक प्रक्रिया को मारना सुरक्षित है (ओएस इसका ख्याल रखता है) - जो धागे के लिए सच नहीं है। तो यह _is_ अधिक सुरुचिपूर्ण समाधान है, और यह है, मुझे लगता है, एकमात्र समाधान, यदि तीसरे पक्ष के जेएनआई लिब कुछ बाहरी संसाधनों का उत्पादन नहीं करने के लिए जाना जाता है, जैसे फाइलें, जिन्हें प्रक्रिया समाप्त होने पर ओएस द्वारा साफ़ नहीं किया जाएगा। – BegemoT

संबंधित मुद्दे