2009-10-26 15 views
16

टी 2 रीड्यूम विधि को डी 2010 में बहिष्कृत किया गया है। तो, मैंने सोचा कि यह अब इस तरह काम करना चाहिए:डेल्फी 2010 में निलंबित धागा फिर से शुरू करना?

TMyThread = class (TThread) 
protected 
    Execute; override; 
public 
    constructor Create; 
end; 
... 

TMyThread.Create; 
begin 
    inherited Create (True); 
    ... 
    Start; 
end; 

दुर्भाग्य से मैं एक अपवाद है ... जो मुझे अजीब लगता है इस तथ्य पर विचार है कि प्रलेखन मुझसे कहता है "एक चल रहा है या supsended धागे पर शुरू कॉल नहीं कर सकता" मिल कि मुझे निलंबित मोड में बनाए गए धागे पर प्रारंभ करना चाहिए।

मुझे यहां क्या याद आ रही है?

उत्तर

19

The reason is that a Thread is not supposed to start itself

प्रारंभिकरण पूर्ण होने पर थ्रेड कभी नहीं जानता था। निर्माण प्रारंभिक के समान नहीं है (निर्माण हमेशा छोटा और अपवाद मुक्त होना चाहिए; निर्माण के बाद और प्रारंभिक कार्य किया जाता है)।

एक ऐसी ही स्थिति एक TDataSet है: कोई TDataSet निर्माता कभी ओपन बुलाना चाहिए, या सेट सक्रिय: = सच

यह भी देखें blog entry by Wings of Wind

आप चाहिए या तो:

  • TMyThread बनाएं (सही) को फोन करके निलंबित बनाएँ और अपने TMyThread वर्ग
  • बाहर प्रारंभ प्रदर्शन TMyThread गैर suspeneded, यकीन है कि निर्माता बनाएं बनाने करता है पूर्ण प्रारंभ बनाएं , और TThread.AfterConstruction धागा शुरू करें।

TThread उपयोग का स्पष्टीकरण:

मूल रूप से, एक धागा सिर्फ इतना है कि किया जाना चाहिए: संदर्भ जिस पर कोड निष्पादित होने की कैप्सूलीकरण।

निष्पादित वास्तविक कोड (व्यावसायिक तर्क) तब अन्य वर्गों में होना चाहिए।

उन दोनों को decoupling करके, आप बहुत लचीलापन प्राप्त करते हैं, विशेष रूप से कई स्थानों के भीतर से अपने व्यापार तर्क शुरू करना (जो यूनिट परीक्षण लिखते समय बहुत सुविधाजनक है!)।

unit DecoupledThreadUnit; 

interface 

uses 
    Classes; 

type 
    TDecoupledThread = class(TThread) 
    strict protected 
    //1 called in the context of the thread 
    procedure DoExecute; virtual; 
    //1 Called in the context of the creating thread (before context of the new thread actualy lives) 
    procedure DoSetUp; virtual; 
    //1 called in the context of the thread right after OnTerminate, but before the thread actually dies 
    procedure DoTearDown; virtual; 
    protected 
    procedure DoTerminate; override; 
    procedure Execute; override; 
    public 
    constructor Create; 
    procedure AfterConstruction; override; 
    end; 

implementation 

constructor TDecoupledThread.Create; 
begin 
    // create suspended, so that AfterConstruction can call DoSetup(); 
    inherited Create(True); 
end; 

procedure TDecoupledThread.AfterConstruction; 
begin 
    // DoSetUp() needs to be called without the new thread in suspended state 
    DoSetUp(); 
    // this will unsuspend the underlying thread 
    inherited AfterConstruction; 
end; 

procedure TDecoupledThread.DoExecute; 
begin 
end; 

procedure TDecoupledThread.DoSetUp; 
begin 
end; 

procedure TDecoupledThread.DoTearDown; 
begin 
end; 

procedure TDecoupledThread.DoTerminate; 
begin 
    inherited DoTerminate(); 
    // call DoTearDown on in the thread context right before it dies: 
    DoTearDown(); 
end; 

procedure TDecoupledThread.Execute; 
begin 
    // call DoExecute on in the thread context 
    DoExecute(); 
end; 

end. 

तुम भी यह घटना कुछ इस तरह से आधारित बना सकता है:

unit EventedThreadUnit; 

interface 

uses 
    Classes, 
    DecoupledThreadUnit; 

type 
    TCustomEventedThread = class(TDecoupledThread) 
    private 
    FOnExecute: TNotifyEvent; 
    FOnSetUp: TNotifyEvent; 
    FOnTearDown: TNotifyEvent; 
    strict protected 
    procedure DoExecute; override; 
    procedure DoSetUp; override; 
    procedure DoTearDown; override; 
    public 
    property OnExecute: TNotifyEvent read FOnExecute write FOnExecute; 
    property OnSetUp: TNotifyEvent read FOnSetUp write FOnSetUp; 
    property OnTearDown: TNotifyEvent read FOnTearDown write FOnTearDown; 
    end; 

    // in case you want to use RTTI 
    TEventedThread = class(TCustomEventedThread) 
    published 
    property OnExecute; 
    property OnSetUp; 
    property OnTearDown; 
    end; 

implementation 

{ TCustomEventedThread } 

procedure TCustomEventedThread.DoExecute; 
var 
    TheOnExecute: TNotifyEvent; 
begin 
    inherited; 
    TheOnExecute := OnExecute; 
    if Assigned(TheOnExecute) then 
    TheOnExecute(Self); 
end; 

procedure TCustomEventedThread.DoSetUp; 
var 
    TheOnSetUp: TNotifyEvent; 
begin 
    inherited; 
    TheOnSetUp := OnSetUp; 
    if Assigned(TheOnSetUp) then 
    TheOnSetUp(Self); 
end; 

procedure TCustomEventedThread.DoTearDown; 
var 
    TheOnTearDown: TNotifyEvent; 
begin 
    inherited; 
    TheOnTearDown := OnTearDown; 
    if Assigned(TheOnTearDown) then 
    TheOnTearDown(Self); 
end; 

end. 

या की तरह DUnit TTestCase वंशजों के लिए अनुकूल

यह आपको उस के लिए इस्तेमाल कर सकते हैं ढांचे की तरह है इस:

unit TestCaseThreadUnit; 

interface 

uses 
    DecoupledThreadUnit, 
    TestFramework; 

type 
    TTestCaseRanEvent = procedure (Sender: TObject; const TestResult: TTestResult) of object; 
    TTestCaseThread = class(TDecoupledThread) 
    strict private 
    FTestCase: TTestCase; 
    strict protected 
    procedure DoTestCaseRan(const TestResult: TTestResult); virtual; 
    function GetTestCase: TTestCase; virtual; 
    procedure SetTestCase(const Value: TTestCase); virtual; 
    protected 
    procedure DoExecute; override; 
    procedure DoSetUp; override; 
    procedure DoTearDown; override; 
    public 
    constructor Create(const TestCase: TTestCase); 
    property TestCase: TTestCase read GetTestCase write SetTestCase; 
    end; 

implementation 

constructor TTestCaseThread.Create(const TestCase: TTestCase); 
begin 
    inherited Create(); 
    Self.TestCase := TestCase; 
end; 

procedure TTestCaseThread.DoExecute; 
var 
    TestResult: TTestResult; 
begin 
    if Assigned(TestCase) then 
    begin 
    // this will call SetUp and TearDown on the TestCase 
    TestResult := TestCase.Run(); 
    try 
     DoTestCaseRan(TestResult); 
    finally 
     TestResult.Free; 
    end; 
    end 
    else 
    inherited DoExecute(); 
end; 

procedure TTestCaseThread.DoTestCaseRan(const TestResult: TTestResult); 
begin 
end; 

function TTestCaseThread.GetTestCase: TTestCase; 
begin 
    Result := FTestCase; 
end; 

procedure TTestCaseThread.SetTestCase(const Value: TTestCase); 
begin 
    FTestCase := Value; 
end; 

procedure TTestCaseThread.DoSetUp; 
begin 
    if not Assigned(TestCase) then 
    inherited DoSetUp(); 
end; 

procedure TTestCaseThread.DoTearDown; 
begin 
    if not Assigned(TestCase) then 
    inherited DoTearDown(); 
end; 

end. 

--jeroen

+0

तो सही तरीका 'MyThread: = TMyThread.Create' और फिर' MyThread.Start' होगा, 'निष्पादन' की शुरुआत में सभी लंबा प्रारंभिक प्रदर्शन करेगा? कक्षा के उपयोगकर्ताओं को यह पता होना चाहिए कि थ्रेड को मैन्युअल रूप से शुरू करने की आवश्यकता है? (दस्तावेज़ीकरण एक तरफ) – jpfollenius

+0

चूंकि वर्ग के उपयोगकर्ताओं को बहुप्रचारित प्रोग्रामिंग में पूरी तरह से ज्ञात माना जाता है, यह एक मामूली विस्तार होना चाहिए। मल्टीथ्रेडेड क्लास से उन लोगों से बचें जो मल्टीथ्रेड प्रोग्रामिंग की सभी बारीकियों से निपटने के लिए तैयार नहीं हैं। –

+0

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

14

संक्षिप्त उत्तर: विरासत में कॉल करें (झूठी) बनाएं और स्टार्ट को छोड़ दें!

गैर-निर्माण-निलंबित धागे की वास्तविक शुरुआत बाद के निर्माण में की जाती है, जिसे सभी रचनाकारों के नाम से बुलाया जाता है।

+2

ध्यान दें कि यह अपेक्षाकृत हालिया विकास है। जैसे ही उत्तराधिकारी कन्स्ट्रक्टर चलाना समाप्त हो जाता है, पुराने संस्करण वास्तव में चलना शुरू हो जाएंगे। इसके लिए वास्तव में एक आसान कामकाज है, हालांकि: विरासत कन्स्ट्रक्टर ** अंतिम ** को कॉल करें। (इसे पहले कॉल करने का कोई कारण नहीं है; एक वंशज को शायद ही कभी किसी भी संपत्ति मूल्यों की आवश्यकता होती है जो बेस टीटीएचडी कन्स्ट्रक्टर सेट करता है।) –

+2

उन्होंने स्पष्ट रूप से डी 2010 का उल्लेख किया ... –

+0

नहीं, @ जेन, विरासत में निर्माता आपके सेट को स्थापित करने की स्वतंत्रता नहीं है खेतों में शून्य। यह आपके फ़ील्ड तक नहीं पहुंच सकता है क्योंकि यह उन क्षेत्रों से पहले भी लिखा और संकलित किया गया था, इसलिए यह उन्हें संदर्भित नहीं कर सकता है। (यदि आप खुद को साफ़ करने के लिए ज़ीरोमेमरी का उपयोग कर कन्स्ट्रक्टर के बारे में सोच रहे हैं, तो यह * किसी भी * वर्ग में विनाशकारी होगा, न केवल टीटीएचड। सभी फ़ील्ड * किसी भी * कन्स्ट्रक्टर रन से पहले शून्य हो जाते हैं। TObject.InitInstance देखें।) –

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