【多线程】多线程教程之(一)---线程的创建和参数设置

1. 线程的创建

在程序中调用CreateThread函数可以创建一个线程(但是一般不推荐这个函数创建线程,建议使用C++运行库函数_beginthreadex()函数,它里面调用了CreateThread()函数):

[cpp] view plain copy
  1. HANDLE CreateThread(  
  2.  LPSECURITY_ATTRIBUTES lpThreadAttributes,  
  3.  DWORD dwStackSize,  
  4.  LPTHREAD_START_ROUTINE lpStartAddress,  
  5.  LPVOID lpParameter,  
  6. DWORD dwCreationFlags,  
  7. LPDWORD lpThreadId);  

每个参数的含义为:

LPSECURITY_ATTRIBUTES lpThreadAttributes   

         一般设为NULL。该参数是指向一个SECURITY_ATTRIBUTES结构的指针。如果要赋予该线程内核对象缺省的安全属性,可以传递一个NULL。如果希望所有的子进程能够继承该线程对象的句柄,须设定一个SECURITY_ATTRIBUTES结构,它的bInheritHandle成员应初始化为TRUE。

DWORD dwStackSize  

          一般设为0, 该参数指定线程栈的大小。每个线程都拥有它自己的堆栈。如果dwStakSize为0,系统默认保留的栈的空间为1MB。

LPTHREAD_START_ROUTINE lpStartAddress  

           入口函数 该参数用于指定新建线程的入口函数的地址。可以创建多个线程,使用相同的入口函数地址。该入口函数的原型为:

DWORD WINAPI ThreadFunc(LPVOID lpParameter);

        名字可以改变,返回类型和参数相同就可以。

DWORD dwCreationFlags  

        一般为0,该参数控制创建线程的标志,它可以是0或者CREATE_SUSPENDED。如果是0,则新建立的线程在创建完毕后被系统调度程序调度(可能被执行,也可能不被执行,这取决于系统中其他线程的优先级情况)。如果该值为CREATE_SUSPENDED,系统在创建完新线程后,新线程被系统挂起,直到有其他线程执行了带该新线程句柄的ResumeThread()函数后,新线程才被激活。

LPDWORD lpThreadId  

       一般为NULL,该参数指定新线程的ID。在Windows95中,该参数不能为NULL,否则会引起错误,在Windows 2000中该参数可以为NULL。


例1:
创建线程,并传递参数:

  1. DWORD WINAPI SubThread(LPVOID lpParam)  
  2. {  
  3.     TRACE("SubThread,lpParam is:%d\n",lpParam);  
  4.     return 0;  
  5. }  
  6.   
  7. void main()  
  8. {  
  9.     HANDLE hThread=CreateThread(NULL,0,SubThread,(LPVOID)123,0,NULL);  
  10.     Sleep(1000);  
  11.     CloseHandle(hThread);  
  12. }  

例2:
创建挂起的新线程,老线程执行一些工作后,再激活新创建的线程。

  1. DWORD WINAPI SubThread(LPVOID lpParam)  
  2. {  
  3.     TRACE("SubThread,lpParam is:%d\n",lpParam);  
  4.     return 0;  
  5. }  

  6. void main()  
  7. {  
  8.     HANDLE hThread=CreateThread(NULL,0,SubThread,(LPVOID)123,CREATE_SUSPENDED,NULL);  
  9.     TRACE("SubThread is created\n");  
  10.     ResumeThread(hThread);  
  11.     Sleep(1000);  
  12.     CloseHandle(hThread);  
  13. }  

2. 线程的终止
可以使用下面方法来终止线程:

(0)线程函数的返回(这种方法最安全!);
 (1)通过调用ExitThread函数,线程将自己撤销
 (2)同一个进程或者另一个进程中的线程调用TerminateThread函数终止另外一个线程的运行
 (3)包含线程的进程终止运行(主线程退出)。

1、 线程函数返回。

这是确保线程的所有资源被正确地清除的 唯一办法 。当线程函数返回时,如下情况将会发生:
(1)在线程函数中创建的所有C++对象将通过它们的析构函数正确地撤销;
(2)操作系统将正确地释放线程的堆栈使用的内存;
(3)系统将线程的退出代码设置为线程函数的返回值;
(4)系统递减线程内核对象的引用计数。

2、 ExitThread函数。

可以通过在线程中调用ExitThread函数,来强制终止自身线程的运行。原型为:
VOID ExitThread(DWORD dwExitCode);

该函数将终止自身线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++资源(如C++对象)将不被正确地撤销。由于这个原因,最好从线程函数返回,而不是通过调用ExitThread来返回。

3、 TerminateThread函数。调用TerminateThread函数将终止指定线程的运行,原型为:

BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);

//HANDLE hThread—将要终止的线程的句柄

//DWORD dwExitCode—传递给将要终止的线程的退出代码

与ExitThread不同,ExitThread是撤销自身线程,而TerminateThread能够撤销任何线程。

要注意的是,TerminateThread是异步运行的函数,也就是说,它告诉系统要终止指定线程的运行,但是被终止的线程没有收到它被杀死的通知,线程无法正确清理。也即是说它不能保证指定的线程已经撤销,如果要确切地知道被指定的线程是否已经被撤销,请调用WaitForSingleObject等函数。
4、 进程终止运行(主线程退出)
5、 线程 终止运行 时,会发生下列操作:
线程拥有的所有USER对象句柄均被释放。在Windows中,线程所创建的大部分对象归它的进程所有。但是,线程也可以拥有两个USER对象:窗口和钩子。当创建这些对象的线程终止运行时,系统会自动释放这些对象。其它对对象只有在进程终止时才被释放;
线程的退出代码从STILL_ACTIVE改为线程函数返回值或者传递给ExitThread或者TerminateThread的代码;
线程内核对象的状态变为通知(信号)状态
如果线程是进程中最后一个活动线程,进程也被终止;
线程内核对象的使用计数递减1。


自身调用ExitThread退出线程:

  1. class CTest  
  2. {  
  3. private:  
  4.     int m_iId;  
  5. public:  
  6.     CTest(int iId)  
  7.     {  
  8.         m_iId=iId;  
  9.     }  
  10.     virtual ~CTest()  
  11.     {  
  12.         TRACE("ID:%d ~CTest()\n",m_iId);  
  13.     }  
  14. };  
  15.  
  16. DWORD WINAPI SubThread(LPVOID lpParam)  
  17. {  
  18.     CTest obj(1),*pObj;   
  19.     pObj=new CTest(2); //注意,堆分配的资源并不随着线程的退出而自动释放  
  20.     Sleep(500);  
  21.     ExitThread(1000); //接下来的返回语句将得不到执行,obj也不会被析构  
  22.     return 999;   
  23. }  
  24.   
  25. void main()  
  26. {  
  27.     DWORD dwRet;  
  28.     HANDLE hThread=CreateThread(NULL,0,SubThread,NULL,0,NULL);  
  29.     GetExitCodeThread(hThread,&dwRet);  
  30.     TRACE("SubThread exitcode:%d\n",dwRet);//很可能为STILL_ACTIVE  
  31.     Sleep(1000);  
  32.     GetExitCodeThread(hThread,&dwRet);  
  33.     TRACE("SubThread exitcode:%d\n",dwRet);//很可能为1000  
  34.     CloseHandle(hThread);  
  35. }  

调用TerminateThread来终止另外一个线程的运行:

  1. DWORD WINAPI SubThread(LPVOID lpParam)  
  2. {  
  3.     CTest obj(1),*pObj;   
  4.     pObj=new CTest(2); //注意,堆分配的资源并不随着线程的退出而自动释放  
  5.     Sleep(500);  
  6.     return 999;   
  7. }  
  8.   
  9. void main()  
  10. {  
  11.     DWORD dwRet;  
  12.     HANDLE hThread=CreateThread(NULL,0,SubThread,NULL,0,NULL);  
  13.     GetExitCodeThread(hThread,&dwRet);  
  14.     TRACE("SubThread exitcode:%d\n",dwRet); //很可能为STILL_ACTIVE  
  15.     TerminateThread(hThread,1001); //强行终止线程hThread的运行  
  16.     //TerminateThread是异步函数,所以可能此时hThread并没撤销完毕。  
  17.     Sleep(500);   
  18.     GetExitCodeThread(hThread,&dwRet);  
  19.     TRACE("SubThread exitcode:%d\n",dwRet); //很可能为1001  
  20.     CloseHandle(hThread);  
  21. }  


3. 线程的暂停(挂起)与恢复运行
任何线程都可以调用SuspendThread()来暂停另一个线程的运行(只要拥有线程的句柄)。原型为:
DWORD SuspendThread(HANDLE hThread);
返回值是前一次暂停计数,一个线程能够被暂停的最多次数是MAXIMUM_SUSPEND_COUNT,若一个线程被挂起3次,那么它有资格让系统为他分配CPU之前,必须恢复三次!!
参数HANDLE hThread表示将要被挂起的线程
调用ResumeThread()可以让挂起的线程恢复运行。原型为:
DWORD ResumeThread(HANDLE hThread);
返回值是前一次暂停计数,参数HANDLE hThread表示将要被恢复的线程
例:
  1. #include   
  2. #include   
  3.   
  4. DWORD WINAPI ThreadProc(LPVOID lpParam)  
  5. {  
  6.     printf("subThread: lpParam is %d\n", lpParam);  
  7.     return 0;  
  8. }  
  9.   
  10. void main()  
  11. {  
  12.     HANDLE hThread=CreateThread(NULL, 0, ThreadProc, (LPVOID)123, CREATE_SUSPENDED, 0);  
  13.     printf("a new thread is created\n");  
  14.     SuspendThread(hThread);
  15.     Sleep(10000); 
  16.     ResumeThread(hThread);  
  17.     CloseHandle(hThread);  
  18. }

4. 线程的优先级函数SetThreadPriority:
[cpp]  view plain  copy
  1. HANDLE g_hThread1=NULL;  
  2. HANDLE g_hThread2=NULL;  
  3.  
  4. void ConsumeCPU()  
  5. {  
  6.     char szBuf[8192];  
  7.     for(int i=0;i<200000;i++)  
  8.     {  
  9.         sprintf(szBuf,"%d",rand());  
  10.         memset(szBuf,0,sizeof(szBuf));  
  11.     }  
  12. }  
  13.   
  14. DWORD WINAPI SubThread(LPVOID lpParam)  
  15. {  
  16.     int iLoop=0;  
  17.     while(TRUE)  
  18.     {  
  19.         ConsumeCPU();  
  20.         TRACE("Thread %X loop:%d\n",GetCurrentThreadId(),iLoop++);  
  21.     }  
  22.     return 0;  
  23. }  
  24.   
  25. void CMy0621Dlg::OnOK()   
  26. {  
  27.     g_hThread1=CreateThread(NULL,0,SubThread,NULL,0,NULL);  
  28.     g_hThread2=CreateThread(NULL,0,SubThread,NULL,0,NULL);  
  29.     SetThreadPriority(g_hThread1,THREAD_PRIORITY_LOWEST);  
  30.     SetThreadPriority(g_hThread2,THREAD_PRIORITY_LOWEST);  
  31. }  
  32.    
  33. void CMy0621Dlg::OnButtonReduce()   
  34. {  
  35.     SetThreadPriority(g_hThread1,THREAD_PRIORITY_IDLE);  
  36. }  
  37.    
  38. void CMy0621Dlg::OnButtonRestore()   
  39. {  
  40.     SetThreadPriority(g_hThread1,THREAD_PRIORITY_LOWEST);  
  41. }  



你可能感兴趣的:(【多线程】多线程教程之(一)---线程的创建和参数设置)