डेल्फी थ्रेड पूल उदाहरण AsyncCalls का उपयोग करना

यह मेरा अगला परीक्षण प्रोजेक्ट है कि डेल्फी के लिए थ्रेडिंग लाइब्रेरी मेरे "फ़ाइल स्कैनिंग" कार्य के लिए मुझे सबसे अच्छा सूट करेगा, मैं कई थ्रेड्स में / थ्रेड पूल में प्रक्रिया करना चाहूंगा।

अपने लक्ष्य को दोहराने के लिए: 500-2000 + फाइलों की मेरी अनुक्रमिक "फाइल स्कैनिंग" को नॉन थ्रेडेड एप्रोच से थ्रेडेड में बदलना। मुझे एक बार में 500 धागे नहीं चलाने चाहिए, इस प्रकार एक थ्रेड पूल का उपयोग करना चाहिए। थ्रेड पूल एक कतार जैसा वर्ग है जो कतार से अगले कार्य के साथ कई चलने वाले धागे खिलाता है।

पहला (बहुत ही बुनियादी) प्रयास केवल टीथ्रेड क्लास को बढ़ाकर और एक्सक्यूट विधि (मेरे थ्रेडेड स्ट्रिंग पैरेंट) को लागू करने के द्वारा किया गया था।

चूंकि डेल्फी में एक थ्रेड पूल क्लास बॉक्स से बाहर लागू नहीं है, इसलिए मैंने अपने दूसरे प्रयास में प्रिमोज़ गैब्रिएलसिक द्वारा ओम्नीथ्रेडल्यूट्स का उपयोग करने की कोशिश की है।

OTL शानदार है, एक पृष्ठभूमि में एक कार्य को चलाने के लिए zillion तरीके हैं, एक तरीका है अगर आप अपने कोड के टुकड़ों के थ्रेडेड निष्पादन को सौंपने के लिए "फायर-एंड-भूल" दृष्टिकोण चाहते हैं।

एंड्रियास हौसलाडेन द्वारा AsyncCalls

instagram viewer
नोट: यदि आप पहली बार सोर्स कोड डाउनलोड करते हैं, तो यह अनुसरण करना अधिक आसान होगा।

अपने कुछ कार्यों को थ्रेडेड तरीके से निष्पादित करने के लिए और अधिक तरीकों की खोज करते हुए, मैंने एंड्रियास हॉसडेलन द्वारा विकसित "AsyncCalls.pas" यूनिट को भी आज़माने का फैसला किया है। एंडी AsyncCalls - एसिंक्रोनस फ़ंक्शन कॉल करता है यूनिट एक अन्य पुस्तकालय है जिसमें डेल्फी डेवलपर कुछ कोड को निष्पादित करने के लिए थ्रेडेड दृष्टिकोण को लागू करने के दर्द को कम करने के लिए उपयोग कर सकता है।

एंडी के ब्लॉग से: AsyncCalls के साथ आप एक ही समय में कई फ़ंक्शन निष्पादित कर सकते हैं और उन्हें फ़ंक्शन या विधि में हर बिंदु पर सिंक्रनाइज़ कर सकते हैं जो उन्होंने शुरू किया था ...। AsyncCalls इकाई अतुल्यकालिक कार्यों को कॉल करने के लिए कई प्रकार के फ़ंक्शन प्रोटोटाइप प्रदान करती है ...। यह एक थ्रेड पूल को लागू करता है! स्थापना सुपर आसान है: बस अपनी किसी भी इकाई से एसिंक्लोल्स का उपयोग करें और आपके पास "अलग थ्रेड में निष्पादित करें, मुख्य यूआई को सिंक्रनाइज़ करें, समाप्त होने तक प्रतीक्षा करें" जैसी चीजों तक त्वरित पहुंच है।

उपयोग करने के लिए स्वतंत्र (MPL लाइसेंस) AsyncCalls के अलावा, एंडी भी अक्सर डेल्फी आईडी के लिए अपने स्वयं के सुधार प्रकाशित करता है जैसे "डेल्फी स्पीड अप" तथा "DDevExtensions"मुझे यकीन है कि आपने सुना है (यदि पहले से उपयोग नहीं कर रहा है)।

लड़ाई में AsyncCalls

संक्षेप में, सभी AsyncCall फ़ंक्शन एक IAsyncCall इंटरफ़ेस लौटाते हैं जो फ़ंक्शन को सिंक्रनाइज़ करने की अनुमति देता है। IAsnycCall निम्नलिखित विधियों को उजागर करता है:

 //v 2.98 of asynccalls.pas
IAsyncCall = इंटरफ़ेस
// फ़ंक्शन समाप्त होने तक प्रतीक्षा करता है और रिटर्न वैल्यू देता है
समारोह सिंक: पूर्णांक;
// एसिंक्रोनस फ़ंक्शन समाप्त होने पर सही लौटाता है
समारोह समाप्त: बूलियन;
// अतुल्यकालिक फ़ंक्शन का रिटर्न मान लौटाता है, जब समाप्त TRUE है
समारोह रिटर्नवैल्यू: पूर्णांक;
// AsyncCalls को बताता है कि असाइन किए गए फ़ंक्शन को वर्तमान थ्रेड में निष्पादित नहीं किया जाना चाहिए
प्रक्रिया ForceDifferentThread;
समाप्त;

दो पूर्णांक मापदंडों (IAsyncCall को वापस करने) की अपेक्षा करने वाली विधि के लिए एक उदाहरण कॉल यहां है:

 TAsyncCalls। आह्वान करें (AsyncMethod, i, random (500));
समारोह TAsyncCallsForm। AsyncMethod (taskNr, sleepTime: पूर्णांक): पूर्णांक;
शुरू
परिणाम: = नींद का समय;
नींद (स्लीपटाइम);
TAsyncCalls। VCLInvoke (
प्रक्रिया
शुरू
लॉग (प्रारूप ('किया> nr:% d / कार्य:% d / शयन:% d', [tasknr, asyncHelper) टास्काउंट, स्लीपटाइम]);
समाप्त);
समाप्त;

TAsyncCalls। VCLInvoke आपके मुख्य थ्रेड (एप्लिकेशन का मुख्य थ्रेड - आपके एप्लिकेशन उपयोगकर्ता इंटरफ़ेस) के साथ सिंक्रनाइज़ेशन करने का एक तरीका है। VCLInvoke तुरंत लौटता है। अनाम विधि को मुख्य थ्रेड में निष्पादित किया जाएगा। इसमें VCLSync भी है जो गुमनाम तरीके से मुख्य थ्रेड में कॉल किए जाने पर वापस लौटता है।

AsyncCalls में थ्रेड पूल

मेरे "फ़ाइल स्कैनिंग" कार्य पर वापस जाएं: फीड करते समय (लूप के लिए) एस्सेंकोल्स थ्रेड पूल TAsyncCalls की श्रृंखला के साथ। आह्वान () कॉल, कार्यों को पूल में आंतरिक में जोड़ा जाएगा और "समय आने पर" निष्पादित हो जाएगा (जब पहले जोड़ा कॉल समाप्त हो गया है)।

सभी IAsyncCalls समाप्त होने तक प्रतीक्षा करें

Asyncccult में परिभाषित AsyncMultiSync फ़ंक्शन समाप्त होने के लिए async कॉल (और अन्य हैंडल) की प्रतीक्षा करता है। कुछ और है अतिभारित AsyncMultiSync को कॉल करने के तरीके, और यहां सबसे सरल है:

समारोह AsyncMultiSync (स्थिरांक सूची: की श्रंखला IAsyncCall; WaitAll: बूलियन = सच; मिलीसेकंड: कार्डिनल = इन्फिनिटी): कार्डिनल; 

अगर मुझे "सभी प्रतीक्षा करें" लागू करना है, तो मुझे IAsyncCall की एक सरणी भरने और 61 के स्लाइस में AsyncMultiSync करने की आवश्यकता है।

मेरा AsnycCalls हेल्पर

यहाँ TAsyncCallsHelper का एक टुकड़ा है:

चेतावनी: आंशिक कोड! (डाउनलोड के लिए पूर्ण कोड उपलब्ध)
का उपयोग करता है AsyncCalls;
प्रकार
TIAsyncCallArray = की श्रंखला IAsyncCall;
TIAsyncCallArrays = की श्रंखला TIAsyncCallArray;
TAsyncCallsHelper = कक्षा
निजी
fTasks: TIAsyncCallArrays;
संपत्ति कार्य: TIAsyncCallArrays पढ़ना fTasks;
जनता
प्रक्रिया AddTask (स्थिरांक कॉल: IAsyncCall);
प्रक्रिया WaitAll;
समाप्त;
चेतावनी: आंशिक कोड!
प्रक्रिया TAsyncCallsHelper। WaitAll;
वर
i: पूर्णांक;
शुरू
के लिये i: = उच्च (कार्य) downto कम (कार्य) कर
शुरू
AsyncCalls। AsyncMultiSync (कार्य [i]);
समाप्त;
समाप्त;

इस तरह से मैं 61 (MAXIMUM_ASYNC_WAIT_OBJECTS) - यानी IAsyncCall के सरणियों की प्रतीक्षा कर रहा हूं।

उपरोक्त के साथ, थ्रेड पूल को खिलाने के लिए मेरा मुख्य कोड इस तरह दिखता है:

प्रक्रिया TAsyncCallsForm.btnAddT मास्क (प्रेषक: TObject);
स्थिरांक
nrItems = 200;
वर
i: पूर्णांक;
शुरू
asyncHelper। MaxThreads: = 2 * सिस्टम। CPUCount;
ClearLog ( 'शुरू करने');
के लिये i: = 1 से nrItems कर
शुरू
asyncHelper। AddTask (TAsyncCalls) आह्वान करें (AsyncMethod, i, random (500)));
समाप्त;
लॉग ('सभी में');
// सभी प्रतीक्षा करें
//asyncHelper.WaitAll;
// या "रद्द करें सभी" बटन पर क्लिक करके सभी को रद्द करने की अनुमति न दें:

जबकि नहीं asyncHelper। सब खत्म कर आवेदन। ProcessMessages;
लॉग ( 'समाप्त');
समाप्त;

सब रद्द करो? - AsyncCalls.pas को बदलना होगा :(

मैं उन कार्यों को "रद्द" करने का एक तरीका भी चाहूंगा जो पूल में हैं लेकिन उनके निष्पादन की प्रतीक्षा कर रहे हैं।

दुर्भाग्य से, AsyncCalls.pas थ्रेड पूल में जुड़ने के बाद एक कार्य को रद्द करने का एक सरल तरीका प्रदान नहीं करता है। कोई IAsyncCall नहीं है। रद्द करें या IAsyncCall। DontDoIfNotAlreadyExecuting या IAsyncCall। मेरी परवाह न करें।

इस काम के लिए मुझे AsyncCalls.pas को कम से कम बदलने की कोशिश करके बदलना पड़ा - इसलिए जब एंडी ने एक नया संस्करण जारी किया तो मुझे अपने "रद्द कार्य" विचार के लिए केवल कुछ पंक्तियों को जोड़ना होगा काम कर रहे।

यहाँ मैंने क्या किया है: मैंने IAsyncCall में एक "प्रक्रिया रद्द" जोड़ दी है। रद्द करने की प्रक्रिया "FCancelled" (जोड़ा) फ़ील्ड सेट करती है जो चेक हो जाती है जब पूल कार्य को निष्पादित करना शुरू करता है। मुझे IAsyncCall को थोड़ा बदलने की आवश्यकता थी। समाप्त (ताकि रद्द होने पर भी कॉल रिपोर्ट समाप्त हो जाए) और TAsyncCall। InternExecuteAsyncCall प्रक्रिया (यदि इसे रद्द कर दिया गया है तो कॉल निष्पादित करने के लिए नहीं)।

आप उपयोग कर सकते हैं WinMerge आसानी से एंडी के मूल asynccall.pas और मेरे परिवर्तित संस्करण (डाउनलोड में शामिल) के बीच अंतर का पता लगाने के लिए।

आप पूर्ण स्रोत कोड डाउनलोड करके देख सकते हैं।

इकबालिया बयान

नोटिस! :)

 CancelInvocation विधि AsyncCall को आह्वान करने से रोकती है। यदि AsyncCall पहले से ही संसाधित है, तो कॉल रद्द करें InInvocation का कोई प्रभाव नहीं है और रद्द किया गया फ़ंक्शन तब वापस आ जाएगा जब AsyncCall रद्द नहीं किया गया था।
रद्द विधि सत्य है यदि AsyncCall को रद्द कर दिया गया है।
भूल जाओ विधि आंतरिक AsyncCall से IAsyncCall इंटरफ़ेस को अनलिंक करता है। इसका अर्थ है कि यदि IAsyncCall इंटरफ़ेस का अंतिम संदर्भ चला गया है, तो एसिंक्रोनस कॉल को अभी भी निष्पादित किया जाएगा। फोर्ज को कॉल करने के बाद कॉल करने पर इंटरफ़ेस के तरीके एक अपवाद को फेंक देंगे। Async फ़ंक्शन को मुख्य थ्रेड में कॉल नहीं करना चाहिए क्योंकि यह TThread के बाद निष्पादित किया जा सकता है। सिंक्रोनाइज़ / क्यू मैकेनिज्म को RTL द्वारा बंद कर दिया गया था जो मृत लॉक का कारण बन सकता है।

हालाँकि, अगर आप अभी भी मेरे AsyncCallsHelper से लाभ उठा सकते हैं, तो आपको "asyncHelper" के साथ समाप्त होने के लिए सभी async कॉल की प्रतीक्षा करने की आवश्यकता है। WaitAll "; या यदि आपको "रद्द करें" की आवश्यकता है।