天天看點

wxWidgets開發之線程wxThread

這節分享一下wxWidgets中的線程開發。在wxWidgets中有兩種類型的線程:分離式跟聯合,它們模仿POSIX線程API實作。如果我們查閱wxWidgets線程源碼會發現如下幾個win32線程API接口函數,就知道了其實我們對wxWidgets線程開發本質就是對win32開發。

HANDLE WINAPI CreateThread( _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ SIZE_T dwStackSize, _In_ LPTHREAD_START_ROUTINE lpStartAddress, _In_opt_ LPVOID lpParameter, _In_ DWORD dwCreationFlags, _Out_opt_ LPDWORD lpThreadId ); 

DWORD WINAPI SuspendThread(_In_HANDLE hThread);   //對應着 線程Pause()函數

::TlsSetValue(g_tlsUsedTime, (LPVOID)dwStart) ;//線程本地存儲

DWORD WINAPI ResumeThread(_In_ HANDLE hThread); //對應着Resume()函數

BOOLTerminateThread( HANDLEhThread,DWORDdwExitCode);//對應着Kill()函數

這節我們隻講分離式:建立分離式線程 必須建立在堆上并且不能調用Wait函數,也不要建立全局線程對象,因為它們将在構造函數執行的時候配置設定記憶體,由此帶來記憶體檢測系統出現一些問題。要使用線程首先要實作一個wxThread的派生類,并且重載其純虛函數virtual ExitCode  Entry ()=0,該函數是線程用來具體執行的主要事情。先來看看線程代碼:

wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxThreadEvent);
wxDECLARE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent);
class MyFrame;
class MyThread : public wxThread
{
public:
    MyThread(MyFrame *handler): wxThread(wxTHREAD_DETACHED)
    { 
        m_pHandler = handler 
    }
    ~MyThread();
protected:
    virtual ExitCode Entry();
    MyFrame *m_pHandler;
};
class MyFrame : public wxFrame
{
public:
    ...
    ~MyFrame()
    {
        // it's better to do any thread cleanup in the OnClose()
        // event handler, rather than in the destructor.
        // This is because the event loop for a top-level window is not
        // active anymore when its destructor is called and if the thread
        // sends events when ending, they won't be processed unless
        // you ended the thread from OnClose.
        // See @ref overview_windowdeletion for more info.
    }
    ...
    void DoStartThread();
    void DoPauseThread();
    // a resume routine would be nearly identic to DoPauseThread()
    void DoResumeThread() { ... }
    void OnThreadUpdate(wxThreadEvent&);
    void OnThreadCompletion(wxThreadEvent&);
    void OnClose(wxCloseEvent&);
protected:
    MyThread *m_pThread;
    wxCriticalSection m_pThreadCS;    // protects the m_pThread pointer
    wxDECLARE_EVENT_TABLE();
};
//事件表聲明定義
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_CLOSE(MyFrame::OnClose)
    EVT_MENU(Minimal_Start,  MyFrame::DoStartThread)
    EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_UPDATE, MyFrame::OnThreadUpdate)
    EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_MYTHREAD_COMPLETED, MyFrame::OnThreadCompletion)
wxEND_EVENT_TABLE()
wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_COMPLETED, wxThreadEvent)
wxDEFINE_EVENT(wxEVT_COMMAND_MYTHREAD_UPDATE, wxThreadEvent)
void MyFrame::DoStartThread()
{
    m_pThread = new MyThread(this);
    if ( m_pThread->Run() != wxTHREAD_NO_ERROR )
    {
        wxLogError("Can't create the thread!");
        delete m_pThread;
        m_pThread = NULL;
    }
    // after the call to wxThread::Run(), the m_pThread pointer is "unsafe":
    // at any moment the thread may cease to exist (because it completes its work).
    // To avoid dangling pointers OnThreadExit() will set m_pThread
    // to NULL when the thread dies.
}
wxThread::ExitCode MyThread::Entry()
{
    while (!TestDestroy())
    {
        // ... do a bit of work...
        wxQueueEvent(m_pHandler, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_UPDATE));
    }
    wxQueueEvent(m_pHandler, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_COMPLETED));
    return (wxThread::ExitCode)0;     // success
}
MyThread::~MyThread()
{
    wxCriticalSectionLocker enter(m_pHandler->m_pThreadCS);
    // the thread is being destroyed; make sure not to leave dangling pointers around
    m_pHandler->m_pThread = NULL;
}
void MyFrame::OnThreadCompletion(wxThreadEvent&)
{
    wxMessageOutputDebug().Printf("MYFRAME: MyThread exited!\n");
}
void MyFrame::OnThreadUpdate(wxThreadEvent&)
{
    wxMessageOutputDebug().Printf("MYFRAME: MyThread update...\n");
}
void MyFrame::DoPauseThread()
{
    // anytime we access the m_pThread pointer we must ensure that it won't
    // be modified in the meanwhile; since only a single thread may be
    // inside a given critical section at a given time, the following code
    // is safe:
    wxCriticalSectionLocker enter(m_pThreadCS);
    if (m_pThread)         // does the thread still exist?
    {
        // without a critical section, once reached this point it may happen
        // that the OS scheduler gives control to the MyThread::Entry() function,
        // which in turn may return (because it completes its work) making
        // invalid the m_pThread pointer
        if (m_pThread->Pause() != wxTHREAD_NO_ERROR )
            wxLogError("Can't pause the thread!");
    }
}
void MyFrame::OnClose(wxCloseEvent&)
{
    {
        wxCriticalSectionLocker enter(m_pThreadCS);
        if (m_pThread)         // does the thread still exist?
        {
            wxMessageOutputDebug().Printf("MYFRAME: deleting thread");
            if (m_pThread->Delete() != wxTHREAD_NO_ERROR )
                wxLogError("Can't delete the thread!");
        }
    }       // exit from the critical section to give the thread
            // the possibility to enter its destructor
            // (which is guarded with m_pThreadCS critical section!)
    while (1)
    {
        { // was the ~MyThread() function executed?
            wxCriticalSectionLocker enter(m_pThreadCS);
            if (!m_pThread) break;
        }
        // wait for thread completion
        wxThread::This()->Sleep(1);
    }
    Destroy();
}
           

這段代碼自定義了兩個線程事件類型并且采用異步發送事件、線上程入口函數中循環中進行一個周期性的調用TestDestroy()函數、使用臨界區實作線程同步。在wxWIdets中實作線程同步有三大法寶:wxMutex、wxCirticalSection、wxCondition。後面我會單獨分享條件變量在接收資料、處理資料的相應業務場景展開一篇分享。

關于分離式線程的資源釋放:隻有線上程工作正常的情況下,我們一般調用 Delete函數,是以例子中才進行一個周期性的檢查線程是否正常(TestDestroy)。