2012-03-11 12 views
5

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

procedure TForm1.ButtonMereniClick(Sender: TObject); 
var 
    iterator: Integer; 
begin  
    iterator := 1; 
    repeat  
    //write some values stored int arrays to labels on form 
    LabelTeplota.Caption:='Teplota: '+FloatToStr(poleTeplota[iterator]); 
    LabelVlhkost.Caption:='Vlhkost: '+FloatToStr(poleVlhkost[iterator]); 
    LabelTlak.Caption:='Atmosférický tlak: '+FloatToStr(poleTlak[iterator]); 
    LabelRychlost.Caption:='Rychlost větru: '+FloatToStr(poleRychlost[iterator]); 
    LabelRychlost.Caption:='Rychlost větru: '+FloatToStr(poleRychlost[iterator]); 
    LabelIterator.Caption:='iterator: '+IntToStr(iterator); 
    Sleep(500);//should be 5000 
    Inc(iterator); 
    until iterator = 20; 
end; 
+11

मैं इस अधिकतम सीमा से रहता हूं: "यदि आपको नींद() का उपयोग करने की आवश्यकता महसूस होती है, तो आप इसे गलत कर रहे हैं।" –

+0

@ निक वास्तव में। मेरा समकक्ष है "कोई समस्या नहीं है जिसके लिए नींद समाधान है।" –

+5

@ निकहोडजेस इत्यादि। फिर, क्या आप एक विराम के लिए आवश्यकता को पूरा करते हैं जब थ्रेड स्टैक पर कई स्तर गहरे होते हैं? मुझे लगता है - 'प्रक्रियाओं को एक राज्य-मशीन के रूप में प्रक्रियात्मक रूप से लिखित स्पेक को फिर से कार्यान्वित करने में व्यतीत करें, बस नींद() कॉल के बजाए टाइमर का उपयोग किया जा सके। इसमें कोई संदेह नहीं है कि जीयूआई हैंडलर में एपी + नींद() लूप एक विशेष रूप से लंगड़ा दुरुपयोग है, लेकिन 'नींद का दुरुपयोग किया जा सकता है, इसलिए इसका उपयोग न करें' तर्क गलत है .. 'समर्थन करना मुश्किल है।' –

उत्तर

19

जीयूआई धागा में Sleep समारोह का उपयोग न करें, क्योंकि आप सामान्य संदेश प्रसंस्करण अवरुद्ध कर रहे हैं और अपने आवेदन अजीब व्यवहार।

यह स्पष्ट नहीं है कि आप अपने कोड में Sleep का उपयोग क्यों करते हैं। शायद आपको लूप का उपयोग करने के बजाय TTimer घटक के OnTimer ईवेंट हैंडलर से लेबल अपडेट करना चाहिए।

+2

+1 यह सही उत्तर है। जीयूआई थ्रेड को अवरुद्ध करना हमेशा एक खराब कदम है। एक टाइमर का प्रयोग करें। –

0

यदि आपको देरी या "नींद" प्रकार का फ़ंक्शन उपयोग करना चाहिए, तो आप प्रक्रिया मैसेज के साथ एक प्रक्रिया का उपयोग कर सकते हैं। इसका उपयोग करने के लिए कुछ plusses और minuses हैं, लेकिन मैं कई अवसरों पर सफलतापूर्वक कोई प्रभाव नहीं पड़ा है। मुझे पता है कि यहां अन्य लोग हैं जो प्रोसेस मैसेज पर बेहतर टिप्पणी कर सकते हैं।

Procedure Delay(MSecs: Cardinal); 
var 
FirstTick, CurrentTick : Cardinal; 
Done : Boolean; 
begin 
Done := FALSE; 
FirstTick := GetTickCount; 
While Not Done do 
    begin 
    Application.ProcessMessages; 
    CurrentTick := GetTickCount; 
    If Int64(CurrentTick) - Int64(FirstTick) < 0 Then 
    begin 
    If CurrentTick >= (Int64(FirstTick) - High(Cardinal) + MSecs) Then 
     Done := TRUE; 
     End 
     Else 
     If CurrentTick - FirstTick >= MSecs Then 
      Done := TRUE; 
    end; 
end; 

// Below for a service 

procedure YourSvrSvc.ProcessMessages; 
var 
    Msg: TMsg; 
begin 
    if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then 
    begin 
    TranslateMessage(Msg); 
    DispatchMessage(Msg); 
    end; 
end; 

Procedure YOURSvrSvc.Delay(MSecs: Cardinal); 
var 
FirstTick, CurrentTick : Cardinal; 
Done : Boolean; 
begin 
Done := FALSE; 
FirstTick := GetTickCount; 
While Not Done do 
    begin 
    YOURSvrSvc.ProcessMessages; 
    CurrentTick := GetTickCount; 
    If Int64(CurrentTick) - Int64(FirstTick) < 0 Then 
    begin 
    If CurrentTick >= (Int64(FirstTick) - High(Cardinal) + MSecs) Then 
     Done := TRUE; 
     End 
     Else 
     If CurrentTick - FirstTick >= MSecs Then 
      Done := TRUE; 
    end; 
end; 
+5

प्रक्रिया मैसेज आपको अपने ईवेंट हैंडलर को फिर से प्रवेश करने के लिए कॉल करता है। –

+0

@ डेविड हाँ सर! मैंने उल्लेख किया कि प्लस और माइनस हैं, लेकिन मैंने सोचा कि ओपी के दायरे से बाहर जा रहा है। लेकिन उसे एक विकल्प दिया। – John

+2

मैं केवल प्रक्रिया मैसेज का उपयोग करने के लिए डाउनसाइड्स देखता हूं। –

0

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

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

वहाँ एक धागा बनाने के लिए कुछ तरीके है, लेकिन मैं इस सरल बनाने की कोशिश करता हूँ ...

type 
    TMyThread = class; 

    TMyThreadEvent = procedure(Sender: TObject; const Val1, Val2: String) of object; 

    TMyThread = class(TThread) 
    private 
    FValue1: Integer; 
    FValue2: Integer; 
    FString1: String; 
    FString2: String; 
    FOnChange: TMyThreadEvent; 
    procedure SYNC_OnChange; 
    protected 
    procedure Execute; override; 
    public 
    constructor Create; 
    property Value1: Integer read FValue1 write FValue1; 
    property Value2: Integer read FValue2 write FValue1; 
    property String1: String read FString1; 
    property String2: String read FString2; 
    property OnChange: TMyThreadEvent read FOnChange write FOnChange; 
    end; 

    ... 

    constructor TMyThread.Create; 
    begin 
    inherited Create(False); 
    FValue1 := '0'; 
    FValue2 := '0'; 
    end; 

    procedure TMyThread.Execute; 
    var 
    S1, S2: String; 
    DoChange: Bool; 
    begin 
    DoChange:= False; 
    FValue2:= DoSomeBigCalculation(FValue1); //Some random big calculation 
    S1:= FormatFloat('#,##0.#', FValue1); 
    S2:= FormatFloat('#,##0.#', FValue2); 
    if (S1 <> FString1) then begin 
     FString1:= S1; 
     DoChange:= True; 
    end; 
    if (S2 <> FString2) then begin 
     FString2:= S2; 
     DoChange:= True; 
    end; 
    if DoChange then 
     Synchronize(SYNC_OnChange); 
    end; 

    procedure TMyThread.SYNC_OnChange; 
    begin 
    if assigned(FOnChange) then 
     FOnChange(Self, FString1, FString2); 
    end; 

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

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

+0

पीएस - आप इसे एक कदम आगे ले सकते हैं और अलग-अलग आयोजन कर सकते हैं, प्रत्येक संपत्ति के लिए एक। फिर 'ऑनटेप्लोटा', ​​'ऑनव्लकोस्ट' इत्यादि जैसी घटनाएं होंगी जो नई मूल्य। –

1

मैंने एप्लिकेशन को धीमा किए बिना या सबसे खराब मामले में सिस्टम को "धीरे-धीरे" कुछ मूल्यों (ताकि उपयोगकर्ता प्रगति देख सकें) अपडेट करने के लिए दो टाइमर का उपयोग किया।

// "weights" for old and new value; I was working on integers 
var 
    Wa: integer = 1; 
    Wb: integer = 9; 
    Wt: integer = 10; 

// interval of 1000 ms, of course 
procedure frmMain.T1000Timer(Sender: TObject); 
begin 
    Wa:= 1; 
    nValue:= calculate_new_value; 
end; 

// 100 ms interval 
procedure frmMain.T100Timer(Sender: TObject); 
begin 
    Wb:= Wt -Wa; 
    // displayed value, ie gauge.progress, or something.Value, etc. 
    sam.Value:= Round((Wb *sam.Value +Wa *nValue) /Wt); 
    if Wa < Wt then Inc(Wa); 
end; 
संबंधित मुद्दे