2013-04-04 28 views
1

TThread後代的工作我基本上有一個選擇:終止線程變量,並將其設置爲NULL

  • 設置FreeOnTerminatetrue它刪除我的TThread後代對象,但不會將其設置爲NULL
  • 做手動,並通過自己刪除它的所有爛攤子

我基本上需要的是一種方式來確定線程是否正在運行,所以我做了以下:

//------------------------------------------------------------------------------ 
// Thread descendant 
//------------------------------------------------------------------------------ 
class TMyThread : public TThread 
    { 
    private: UnicodeString StatusLine; // Used for Synchronize function 

       void __fastcall UpdateGUI(); 
    protected: virtual void __fastcall Execute(); 
    public:     __fastcall TMyThread(); 
    }; 
//------------------------------------------------------------------------------ 
TMyThread *MyThread; 
//------------------------------------------------------------------------------ 

// Thread constructor 
__fastcall TMyThread::TMyThread() : TThread(true) 
{ 
FreeOnTerminate = false; 
Priority  = tpNormal; 
} 

// Synchronize function for Form1 
void __fastcall TMyThread::UpdateGUI() 
{ 
Form1->Label1 = StatusLine; 
} 

// Execute code 
void __fastcall TMyThread::Execute() 
{ 
Sleep(2000); 
StatusLine = "I am almost done!"; 
Synchronize(&UpdateGUI); 
} 

// Thread terminate, delete object, set to NULL 
void __fastcall TForm1::ThreadTerminateIfDone(TMyThread *T) 
{ 
if (T != NULL && WaitForSingleObject(reinterpret_cast<void*>(T->Handle),0) == WAIT_OBJECT_0) 
    { 
    T->Terminate(); 
    T->WaitFor(); 
    delete T; 
    T = NULL; 
    } 
} 

// And initialization part which needs to check if thread is already running 
void __fastcall TForm1::StartOrRestartThread(TObject *Sender) 
{ 
// Remove old thread if done 
ThreadTerminateIfDone(MyThread); 

// Check if thread is running - NULL = not running and terminated or uninitialized 
if (MyThread == NULL) 
    { 
    MyThread = new TMyThread(); 
    MyThread->Start(); 
    } 
else 
    { 
    Application->MessageBox(L"Thread is still running please wait!", L"Error", MB_OK); 
    } 
} 

此代碼按原樣運行。我的問題是:

  • 有沒有辦法簡化這個?我需要在完成後將MyThread設置爲NULL,以便在下次調用啓動/重新啓動之前,對象不在周圍。這不能用FreeOnTerminate設置爲true來完成,因爲它會刪除對象。我只能嘗試訪問對象,然後生成異常(我可以捕捉,但它是愚蠢的)。我真的只需要知道在我初始化或重新啓動MyThread之前是否執行了執行。

  • 我可以重新啓動線程而不終止它(我不需要在程序結束之前刪除對象) - 如果我啓動線程,我會得到「無法在運行或掛起的線程上調用啓動」異常。

回答

1

ThreadTerminateIfDone()功能需要通過參考拿線程指針,否則將無法設置指向正確NULL:

void __fastcall TForm1::ThreadTerminateIfDone(TMyThread* &T) 

隨着中說,如果使用線程的OnTerminate事件來跟蹤線程是否正在運行,則可以完全消除ThreadTerminateIfDone()。當FreeOnTerminate設置爲true,則線程被釋放之前OnTerminate被觸發,例如:

class TMyThread : public TThread 
{ 
private: 
    String StatusLine; // Used for Synchronize function 
    void __fastcall UpdateGUI(); 
protected: 
    virtual void __fastcall Execute(); 
public: 
    __fastcall TMyThread(); 
}; 
//------------------------------------------------------------------------------ 
extern TMyThread *MyThread; 
//------------------------------------------------------------------------------ 

TMyThread *MyThread = NULL; 

__fastcall TMyThread::TMyThread() 
    : TThread(true) 
{ 
    FreeOnTerminate = true; 
    Priority  = tpNormal; 
} 

void __fastcall TMyThread::UpdateGUI() 
{ 
    Form1->Label1 = StatusLine; 
} 

void __fastcall TMyThread::Execute() 
{ 
    Sleep(2000); 
    StatusLine = "I am almost done!"; 
    Synchronize(&UpdateGUI); 
} 

void __fastcall TForm1::StartOrRestartThread(TObject *Sender) 
{ 
    if (MyThread == NULL) 
    { 
     MyThread = new TMyThread(); 
     MyThread->OnTerminate = ThreadTerminated; 
     MyThread->Start(); 
    } 
    else 
    { 
     Application->MessageBox(L"Thread is still running please wait!", L"Error", MB_OK); 
    } 
} 

void __fastcall TForm1::ThreadTerminated(TObject *Sender) 
{ 
    MyThread = NULL; 
} 

爲了回答您的其他問題,如果你想線程可以重啓,那麼你必須改變線程設計了一下,如:

class TMyThread : public TThread 
{ 
private: 
    String StatusLine; // Used for Synchronize function 
    TEvent *RestartEvent; 
    void __fastcall UpdateGUI(); 
protected: 
    virtual void __fastcall Execute(); 
public: 
    bool Busy; 
    __fastcall TMyThread(); 
    __fastcall ~TMyThread(); 
    void __fastcall Restart(); 
}; 
//------------------------------------------------------------------------------ 
extern TMyThread *MyThread; 
//------------------------------------------------------------------------------ 

TMyThread *MyThread = NULL; 

__fastcall TMyThread::TMyThread() 
    : TThread(true) 
{ 
    FreeOnTerminate = true; 
    Priority  = tpNormal; 
    RestartEvent = new TEvent(nil, true, true, ""); 
} 

__fastcall TMyThread::~TMyThread() 
{ 
    delete RestartEvent; 
} 

void __fastcall TMyThread::UpdateGUI() 
{ 
    Form1->Label1 = StatusLine; 
} 

void __fastcall TMyThread::Execute() 
{ 
    while (!Terminated) 
    { 
     if (RestartEvent.WaitFor(1000) == wrSignaled) 
     { 
      if (Terminated) return; 

      RestartEvent.ResetEvent(); 
      Busy = true; 

      StatusLine = "I am doing something!"; 
      Synchronize(&UpdateGUI); 

      Sleep(2000); 

      StatusLine = "I am almost done!"; 
      Synchronize(&UpdateGUI); 

      Busy = false; 
     } 
    } 
} 

void __fastcall TForm1::StartOrRestartThread(TObject *Sender) 
{ 
    if (MyThread == NULL) 
    { 
     MyThread = new TMyThread(); 
     MyThread->OnTerminate = ThreadTerminated; 
     MyThread->Start(); 
    } 
    else if (!MyThread->Busy) 
    { 
     MyThread->Restart(); 
    } 
    else 
    { 
     Application->MessageBox(L"Thread is still running please wait!", L"Error", MB_OK); 
    } 
} 

void __fastcall TForm1::ThreadTerminated(TObject *Sender) 
{ 
    MyThread = NULL; 
} 
+0

我確實嘗試過,但是您確定可以將MyThread設置爲ThreadTerminated事件內部的NULL嗎?如果對象尚未釋放,是否會導致訪問衝突? – Coder12345 2013-04-04 20:44:37

+1

@ Coder12345:是的,我確定。 'OnTerminate'事件是通過'Synchronize()'觸發的,直到'OnTerminate'事件處理程序退出之後,線程對象纔會被釋放。 – 2013-04-04 20:46:45

+0

謝謝雷米,很好的答案,一切都很好。 – Coder12345 2013-04-04 21:27:59

1

在頂端運行在線程while循環,用一些等待以允許其運行一次輪用信號時,是這樣優選的以持續創建/終止/銷燬線程,以及相關的微觀管理,即沒有其他課程是理智的。

創建線程,放在一個while循環中,在頂部等待,發出信號運行,永遠不會終止線程,除非絕對強制。死亡的

電話:

TThread.WaitFor 
TThread.Synchronize 
TThread.Terminate 

非常努力地不使用這些,永遠。

簡單的例子:

TmyThread=class(TThread); 
private; 
    mySimpleEvent:TSimpleEvent; 
public 
    constructor create; 
    procedure go; 
end; 

constructor TmyThread.create; 
begin 
    inherited create(true); 
    mySimpleEvent:=TSimpleEvent.Create; 
    resume; 
end; 

procedure TmyThread.go; 
begin 
    mySimpleEvent.SetEvent; 
end; 

procedure TmyThread.Execute; 
begin 
    while mySimpleEvent.WaitFor(INFINITE) do 
    begin 
    mySimpleEvent.ResetEvent; 
    //code to do your stuff 
    end; 
end; 
+0

我該如何更新沒有'Synchronize'的VCL?線程以「一次性」模式完成,所以一旦完成,它會自動終止,不需要循環。可以避免創建/刪除,但一旦終止我無法重新啓動它 - 我「無法啓動暫停或終止的線程」。有任何想法嗎? – Coder12345 2013-04-04 20:27:42

+1

更新VCL:PostMessage API。線程以「一次性」模式完成 - 停止執行「一次性」模式,並使用一個帶有同步對象的循環,在最上面等待事件或信號量。如果你這樣做,你不需要終止線程,所以你不需要嘗試並重新啓動它 - 只需發出同步對象的信號,while循環內的線程代碼將再次執行。只要永遠保留線程(即直到應用程序終止並且操作系統終止它)。 – 2013-04-04 20:33:40

+0

換句話說,你建議有線程使用100%的CPU運行一個空循環,並且只有在信號發送工作時才做事情?我可以將優先級降低到tpIdle,但仍然會使用100%的CPU。不是非常友善的CPU。這個線程需要連接到服務器,每15分鐘取一次或兩次數據。在空閒時間內循環運行它是沒有意義的。 – Coder12345 2013-04-04 20:37:46