一、实验目的
(1)熟悉windows系统提供的线程创建与撤销系统调用。
(2)掌握windows系统环境下线程的创建与撤销方法。
(3)掌握CreateThread()函数和ExitThread()函数。
二、实验准备
(1).实验在windows XP,VC++6.0环境下进行。在这一步,安装了Windows XP虚拟机,学会了创建一个控制台工程文件。
(2).百度句柄的含义:句柄(handle),有多种意义,第一种解释:句柄是一种特殊的智能指针 。当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄。句柄是Windows用来标志应用程序中建立的或是适用的对象的唯一整数,Windows大量使用了句柄来标识对象。
(3).参照实验指导学习函数原型--CreateThread()函数:--线程创建
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES IpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE IpStartAddress,
LPVOID IpParameter,
DWORD dwCreationFlags,
LPDWORD IpThreadId
);
!!!实例化:HANDLE h1 = CreateThread( NULL, 0, Fun1, NULL, 0, NULL );
<1>.参数IpThreadAttributes指定线程安全属性,当其为NULL时,则线程获取默认安全描述符。
<2>.参数dwStackSize指定线程堆栈的初始大小,以字节为单位。如果此参数为0,则新线程使用可执行文件的默认大小。
<3>.参数IpStartAddress指定由线程执行的自定义函数的指针。
<4>.参数IpParameter指定自定义函数的参数。
<5>.参数dwCreationFlags指定线程创建后所处的状态。该值为0,表示线程创建后立即执行。
<6>.参数IpThreadId指定接收线程标识符的变量的指针。该参数为 NULL,则不返回线程标识符。
---如果函数成功,则返回值是新线程的句柄。如果函数失败,则返回值为NULL。
(4).参照实验指导学习函数原型--ExitThread()函数:--撤销进程
<1>.ExitThread()用于撤销当前进程。
<2>.该函数没有返回值。
<3>.用法举例:ExitThread(0);//参数0表示要撤销进程中的所有线程
(5).参照实验指导学习函数原型--TerminateThread()函数:--终止线程
TerminateThread()用于终止当前线程。该函数与ExitThread()的区别在于,ExitThread()在撤销线程时将该线程所拥有的资源全部归还给系统,而TerminateThread()不归还资源。在实验中用ExitThread()函数居多。
(6).参照实验指导学习函数原型--Sleep()函数:--挂起线程
该函数没有返回值。
(7).参照实验指导学习函数原型--CloseHandle()函数:--关闭句柄
返回值:如果函数调用成功,则返回值为非0值;如果函数调用失败,则返回值为0.若要得到更多的错误信息,调用函数GetLastError()查询。
三、实验内容
(一)实验内容
实验1.使用系统调用CreatThread()创建一个子线程,并在子线程中显示;Thread is Running!.为了能让用户清楚地看到线程的运行情况,使用Sleep()使线程挂起5s,之后使用ExitThread(0)撤销进程.
实验2.模仿教学视频中的程序,改写一个吃苹果进程,采用多线程‘吃苹果’与单一线程‘吃苹果’做对比。了解多线程的运行原理。
(二)主要代码:
实验一:
#include "stdafx.h"
#include "01.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
void ThreadName1();
static HANDLE hHandle1=NULL;
DWORD dwThreadID1;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
hHandle1 = CreateThread( (LPSECURITY_ATTRIBUTES) NULL,
0,
(LPTHREAD_START_ROUTINE) ThreadName1,
(LPVOID) NULL,
0, &dwThreadID1 );
Sleep(5000);
CloseHandle(hHandle1);
ExitThread(0);
return nRetCode;
}
void ThreadName1()
{
printf("Thread is running!");
}
实验二:
#include "stdafx.h"
#include "011.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CWinApp theApp;
using namespace std;
void eatApple(int number){
Sleep((3 - number)*1000);
printf("I'm eatting #%d apples.\n",number);
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
HANDLE h1 = NULL;
HANDLE h2 = NULL;
HANDLE h3 = NULL;
DWORD Th1 = NULL;
DWORD Th2 = NULL;
DWORD Th3 = NULL;
int a=0;
int b=1;
int c=2;
h1 = CreateThread( (LPSECURITY_ATTRIBUTES) NULL,
0,
(LPTHREAD_START_ROUTINE) eatApple,
(LPVOID) a,
0, &Th1 );
h2 = CreateThread( (LPSECURITY_ATTRIBUTES) NULL,
0,
(LPTHREAD_START_ROUTINE) eatApple,
(LPVOID) b,
0, &Th2 );
h3 = CreateThread( (LPSECURITY_ATTRIBUTES) NULL,
0,
(LPTHREAD_START_ROUTINE) eatApple,
(LPVOID) c,
0, &Th3 );
eatApple(a);
eatApple(b);
eatApple(c);
ExitThread(0);
return nRetCode;
}
四、实验结果与总结
实验一总结:
在Windows系统中进程是资源的拥有者,线程是系统调用的基本单位。
进程创建后,其主线程也随即被创建。在该实验中,有创建了一个名为
ThreadName1的子线程,该子线程与主线程并并发的被系统调度。
为了能让用户清楚地看到线程的运行情况,在主线程创建了子线程之后,
将主线程挂起5s,以确保子线程能够运行完毕,
之后调用ExitThread(0)将所有线程撤销。
实验二总结:
在该实验中,创建了Th1 Th2 Th3三个子线程,为了能清楚地看到线程的运行情况,在主线程创建了子线程之后,
打印“I'm eatting apples”以确保能够直观的看出运行情况,
之后调用ExitThread(0)将所有线程撤销。
ExitThread(0)的使用:
void eatApple(int number){
Sleep((3 - number)*1000);
printf("I'm eatting #%d apples.\n",number);
while(1){
printf("#%d is exiting.\n\n",number);
ExitThread(0);
}
}
PS:视频中用的是这种方法,但是我在学习文档中看到ExitThread(0)表示要撤销进程中的所有线程。所以我没用视频中的方法,直接在代码后面加了“ExitThread(0);”语句。