Embarcadero Delphi
जीयूआई उत्तरदायी रखते हुए एक धागा चलाना
खोज…
बैकग्राउंड वर्क के लिए थ्रेड्स का उपयोग करके उत्तरदायी GUI और थ्रेड्स से रिपोर्ट करने के लिए PostMessage
एक लंबी प्रक्रिया को चलाने के दौरान जीयूआई को उत्तरदायी बनाए रखने के लिए जीयूआई को अपनी संदेश कतार, या (पृष्ठभूमि) (कार्यकर्ता) थ्रेड के उपयोग की अनुमति देने के लिए कुछ बहुत विस्तृत "कॉलबैक" की आवश्यकता होती है।
किसी काम को करने के लिए किसी भी संख्या में थ्रेड को मारना आमतौर पर कोई समस्या नहीं है। मज़ा तब शुरू होता है जब आप GUI को इंटरमीडिएट और अंतिम परिणाम दिखाना चाहते हैं या प्रगति पर रिपोर्ट करना चाहते हैं।
जीयूआई में कुछ भी दिखाने के लिए नियंत्रण और / या संदेश कतार / पंप के साथ बातचीत की आवश्यकता होती है। यह हमेशा मुख्य धागे के संदर्भ में किया जाना चाहिए। कभी किसी अन्य सूत्र के संदर्भ में नहीं।
इसे संभालने के कई तरीके हैं।
यह उदाहरण दिखाता है कि आप साधारण थ्रेड्स का उपयोग करके कैसे कर सकते हैं, GUI को थ्रेड इंस्टेंस को एक्सेस करने की अनुमति देने के बाद यह समाप्त हो जाता है कि FreeOnTerminate
को false
सेट FreeOnTerminate
, और जब PostMessage
का उपयोग करके "थ्रेड" किया जाता है, तो रिपोर्टिंग करें।
दौड़ की स्थितियों पर नोट्स: कार्यकर्ता थ्रेड्स के संदर्भ फॉर्म में एक सरणी में रखे जाते हैं। जब एक धागा समाप्त हो जाता है, तो सरणी में संबंधित संदर्भ शून्य-एड हो जाता है।
यह दौड़ की स्थिति का एक संभावित स्रोत है। जैसा कि "रनिंग" बूलियन का उपयोग यह निर्धारित करना आसान है कि क्या अभी भी किसी भी धागे को खत्म करने की आवश्यकता है।
आपको यह तय करने की आवश्यकता होगी कि आपको ताले का उपयोग करके थ्रेस संसाधन की रक्षा करने की आवश्यकता है या नहीं।
इस उदाहरण में, जैसा कि यह खड़ा है, कोई आवश्यकता नहीं है। : वे केवल दो स्थानों में संशोधित कर रहे हैं StartThreads
विधि और HandleThreadResults
विधि। दोनों विधियाँ केवल मुख्य सूत्र के संदर्भ में चलती हैं। जब तक आप इसे इस तरह से रखते हैं और विभिन्न थ्रेड्स के संदर्भ से इन तरीकों को कॉल करना शुरू नहीं करते हैं, उनके लिए दौड़ की स्थिति पैदा करने का कोई तरीका नहीं है।
धागा
type
TWorker = class(TThread)
private
FFactor: Double;
FResult: Double;
FReportTo: THandle;
protected
procedure Execute; override;
public
constructor Create(const aFactor: Double; const aReportTo: THandle);
property Factor: Double read FFactor;
property Result: Double read FResult;
end;
कंस्ट्रक्टर सिर्फ निजी सदस्यों को सेट करता है और फ्रीऑनलेट को फाल्स में सेट करता है। यह आवश्यक है क्योंकि यह मुख्य थ्रेड को उसके परिणाम के लिए थ्रेड आवृत्ति को क्वेरी करने की अनुमति देगा।
निष्पादन विधि इसकी गणना करती है और फिर इसके निर्माण में प्राप्त हैंडल को एक संदेश पोस्ट करने के लिए कहती है:
procedure TWorker.Execute;
const
Max = 100000000;var
i : Integer;
begin
inherited;
FResult := FFactor;
for i := 1 to Max do
FResult := Sqrt(FResult);
PostMessage(FReportTo, UM_WORKERDONE, Self.Handle, 0);
end;
इस उदाहरण में PostMessage
का उपयोग आवश्यक है। PostMessage
"just" मुख्य धागे के संदेश पंप की कतार पर एक संदेश डालता है और इसके संभाले जाने की प्रतीक्षा नहीं करता है। यह प्रकृति में अतुल्यकालिक है। यदि आप SendMessage
का उपयोग करने के लिए थे तो आप अपने आप को अचार बनाने के लिए कोडिंग करेंगे। SendMessage
संदेश को कतार में रखता है और उसके संसाधित होने तक प्रतीक्षा करता है। संक्षेप में, यह तुल्यकालिक है।
कस्टम UM_WORKERDONE संदेश के लिए घोषणाएँ इस प्रकार हैं:
const
UM_WORKERDONE = WM_APP + 1;
type
TUMWorkerDone = packed record
Msg: Cardinal;
ThreadHandle: Integer;
unused: Integer;
Result: LRESULT;
end;
UM_WORKERDONE
const अपने मूल्य के लिए शुरुआती बिंदु के रूप में WM_APP
का उपयोग करता है WM_APP
यह सुनिश्चित किया जा सके कि यह विंडोज या डेल्फी वीसीएल (माइक्रो सॉफ्ट द्वारा अनुशंसित ) द्वारा उपयोग किए गए किसी भी मान के साथ हस्तक्षेप नहीं करता है।
प्रपत्र
धागे शुरू करने के लिए किसी भी रूप का उपयोग किया जा सकता है। आपको केवल निम्नलिखित सदस्यों को इसमें शामिल करना है:
private
FRunning: Boolean;
FThreads: array of record
Instance: TThread;
Handle: THandle;
end;
procedure StartThreads(const aNumber: Integer);
procedure HandleThreadResult(var Message: TUMWorkerDone); message UM_WORKERDONE;
ओह, और उदाहरण कोड एक Memo1: TMemo;
के अस्तित्व को मानता है Memo1: TMemo;
फ़ॉर्म की घोषणाओं में, जिसका उपयोग वह "लॉगिंग और रिपोर्टिंग" के लिए करता है।
जीयूआई को काम शुरू होने पर क्लिक करने से रोकने के लिए FRunning
का उपयोग किया जा सकता है। FThreads
का उपयोग उदाहरण सूचक और निर्मित थ्रेड्स के हैंडल को पकड़ने के लिए किया जाता है।
थ्रेड्स को प्रारंभ करने की प्रक्रिया का बहुत सीधा कार्यान्वयन है। यह एक जांच से शुरू होता है कि क्या पहले से ही धागे का एक सेट पर इंतजार किया जा रहा है। यदि हां, तो यह बाहर निकलता है। यदि नहीं, तो यह ध्वज को सही पर सेट करता है और प्रत्येक को अपने स्वयं के हैंडल के साथ प्रदान करने वाले थ्रेड शुरू करता है ताकि वे जान सकें कि उनके "किए गए" संदेश को कहां पोस्ट किया जाए।
procedure TForm1.StartThreads(const aNumber: Integer);
var
i: Integer;
begin
if FRunning then
Exit;
FRunning := True;
Memo1.Lines.Add(Format('Starting %d worker threads', [aNumber]));
SetLength(FThreads, aNumber);
for i := 0 to aNumber - 1 do
begin
FThreads[i].Instance := TWorker.Create(pi * (i+1), Self.Handle);
FThreads[i].Handle := FThreads[i].Instance.Handle;
end;
end;
थ्रेड का हैंडल भी सरणी में रखा गया है क्योंकि यही वह है जो हमें संदेशों में प्राप्त होता है जो हमें बताता है कि एक थ्रेड किया गया है और थ्रेड के उदाहरण के बाहर होने से इसे एक्सेस करना थोड़ा आसान हो जाता है। थ्रेड के इंस्टेंस के बाहर उपलब्ध हैंडल होने से भी हम FreeOnTerminate
सेट को True
पर उपयोग कर सकते हैं यदि हमें इसके परिणाम प्राप्त करने के लिए इंस्टेंस की आवश्यकता नहीं है (उदाहरण के लिए यदि वे किसी डेटाबेस में संग्रहीत थे)। उस मामले में निश्चित रूप से उदाहरण के लिए एक संदर्भ रखने की आवश्यकता नहीं होगी।
मज़े का हैंडल हैंड्रेड्रेडस्कूल कार्यान्वयन में है:
procedure TForm1.HandleThreadResult(var Message: TUMWorkerDone);
var
i: Integer;
ThreadIdx: Integer;
Thread: TWorker;
Done: Boolean;
begin
// Find thread in array
ThreadIdx := -1;
for i := Low(FThreads) to High(FThreads) do
if FThreads[i].Handle = Cardinal(Message.ThreadHandle) then
begin
ThreadIdx := i;
Break;
end;
// Report results and free the thread, nilling its pointer and handle
// so we can detect when all threads are done.
if ThreadIdx > -1 then
begin
Thread := TWorker(FThreads[i].Instance);
Memo1.Lines.Add(Format('Thread %d returned %f', [ThreadIdx, Thread.Result]));
FreeAndNil(FThreads[i].Instance);
FThreads[i].Handle := nil;
end;
// See whether all threads have finished.
Done := True;
for i := Low(FThreads) to High(FThreads) do
if Assigned(FThreads[i].Instance) then
begin
Done := False;
Break;
end;
if Done then
begin
Memo1.Lines.Add('Work done');
FRunning := False;
end;
end;
यह विधि पहले संदेश में प्राप्त हैंडल का उपयोग करके थ्रेड को देखती है। यदि कोई मिलान पाया गया था, तो वह उदाहरण का उपयोग करके थ्रेड के परिणाम को पुनः प्राप्त करता है और रिपोर्ट करता है ( FreeOnTerminate
False
था, याद है?), और फिर खत्म: उदाहरण को खाली करना और इंस्टेंस संदर्भ और nil के हैंडल दोनों को सेट करना, इस थ्रेड को इंगित करना नहीं है। अधिक प्रासंगिक है।
अंत में यह देखने के लिए जांचता है कि क्या कोई थ्रेड अभी भी चल रहा है। यदि कोई भी पाया जाता है, "सब कर" बताया जाता है और FRunning
करने के लिए ध्वज सेट False
इसलिए काम का एक नया बैच शुरू किया जा सकता।