2013-04-04 88 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