线程(Thread)的概念在一些以前的操作系统中是不存在的例如以前的UNIX和Windows3.X,线程与进程的区别在于子线程与父线程序运行在同一进程空间内,而子进程和父进程则运行在不同的空间。这样一来同一进程内的不同线程间可以直接通过内存交换数据(出于数据同步原因最好不要这样做)。
此外在Win32的定义中一个进程至少拥有一个线程,所以进程也被叫做主线程。在上一节中创建进程时大家也看见了可以在获得进程句柄时也可以获得一个线程句柄。在Win32中线程有两种,窗口线程(GUI Thread)和工作线程(Worker Thread)。窗口线程将可以创建窗口,因为创建窗口后系统会为该窗口分配消息队列,而不会为工作线程分配消息队列,所以工作线程将消耗更少的系统资源。
我们先看看如何创建线程。
在MFC中提供了对线程功能的封装类,CWinThread我们常使用的CWinApp类就是从这个类派生的。通常我们使用CWinThread来创建窗口线程,过程如下:
- 从CWinThread中派生新类。
- 重载CWinThread::InitInstance()函数,在其中创建窗口并将窗口指针赋给m_pMainWnd。
- 如果需要可以重载CWinThread::ExitInstance(),在窗口被销毁时该函数将会被调用。也就是说窗口线程的生命期是于窗口的生命器联系起来的。
//线程类定义 class CGUIThread:public CWinThread { public: CGUIThread(); virtual BOOL InitInstance(void); virtual int ExitInstance(void); }; CGUIThread::CGUIThread() { //设置自动删除 m_bAutoDelete = TRUE; } BOOL CGUIThread::InitInstance(void) { CWnd* pWnd= new CWnd();pWnd->CreateEx(0, AfxRegisterWndClass( CS_HREDRAW|CS_VREDRAW) , "gui thread window", WS_OVERLAPPEDWINDOW|WS_VISIBLE, CRect(0,0,100,100), NULL, 0); m_pMainWnd=pWnd; return (m_pMainWnd!=FALSE); } int CGUIThread::ExitInstance(void) { TRACE("gui thread exit/n"); return CWinThread::ExitInstance(); } //创建GUI线程过程 void CSam_sp_43Dlg::OnGuiT() { CGUIThread *p_thread1= new CGUIThread;p_thread1->CreateThread(); //使用默认参数,由于CGUIThread自动删除,所以不需要保存该指针 }
对于工作线程来讲只需要提供一个线程入口,也就是一个函数地址就可以了。在工作线程被启动后会转入该函数,并且函数退出时线程就会结束。在MFC中我们可以通过
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );参数的含义如下:
- pfunThreadProc:是函数入口地址,函数的原形应该如同:UINT MyControllingFunction( LPVOID pParam );
- pParam:是传递给线程的参数。
- nPriority:表明线程的优先级。常用的有THREAD_PRIORITY_IDLE,THREAD_PRIORITY_NORMAL,THREAD_PRIORITY_TIME_CRITICAL,THREAD_PRIORITY_ABOVE_NORMAL。
- nStackSize:为栈大小,如果为0表示使用系统默认值。
- dwCreateFlags:为创建线程时的标记,为CREATE_SUSPENDED表示线程被创建后被挂起。
- lpSecurityAttrs:为安全属性。
下面是个简单的例子: //工作线程 UINT WorkerThread( LPVOID pParam ) { //接收一个窗口类指针,然后设置窗口标题 CWnd *pstaTimer=(CWnd*)pParam; for(int i=0;i<100;i++) { //TRACE("thread %d/n",i); char szT[100]; sprintf(szT,"worker thread : %d",i); pstaTimer->SetWindowText(szT); Sleep(10); } return 0; // 返回并退出线程 //或者调用void AfxEndThread( UINT nExitCode );来退出 } //创建线程 void CSam_sp_43Dlg::OnWorkT() { //m_staTimer为CStatic 变量。传递窗口类指针 AfxBeginThread(WorkerThread,&m_staTimer); }
在CWinThread类中通过DWORD CWinThread::SuspendThread( )和DWORD CWinThread::ResumeThread( )来挂起和恢复线程运行。通过int CWinThread::GetThreadPriority( )和BOOL CWinThread::SetThreadPriority( int nPriority )来获取和设置线程优先级。
此外CWinThread类中的成员变量:m_hThread和m_nThreadID保存了线程句柄和线程ID号。也可以通过其他API函数对线程进行操作。
API函数BOOL TerminateThread( HANDLE hThread,DWORD dwExitCode );可以在线程外部强制结束一个线程,但这样做是有危险的,因为线程申请某些资源可能没法释放,而且也有可能引起进程的崩溃。所以推荐的方法为设置一个标记当线程检测到后自己退出,而不是采用从外部强制结束线程的方法。
下载本节示范代码 20K