这节分享一下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)。