实验一
一、实验目的
(1)熟悉windows系统提供的线程创建与撤销系统调用.
(2)掌握windows系统环境下线程的创建与撤销方法.
二、实验准备
1.创建线程
CreateThread()函数:
在调用进程的地址空间上创建一个线程,执行指定的函数,并返回新建立的线程的句柄。
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, //为线程指定安全属性
DWORD dwStackSize, //线程堆栈的大小
LPVOID lpparameter, //函数中要传递的参数
DWORD dwCreationFlags, //指定线程创建后所处的状态
LPDWORD lpThread //系统返回的线程标识符
);
2.撤销线程
ExitThread()用于撤销当前进程.
VOID ExitThread(
DWORD dwExitCode); //dwExitCode:指定线程返回码,可以调用GetExitCodeThread()查询返回码的含义
3.终止线程
TerminateThread()用于终止当前线程.
该函数与ExitThread()的区别在于,ExitThread()在撤销线程时将该线程所拥有的资源全部归还给系统,而TerminateThread()不归还资源.
BOOL TerminateThread(
HANDLE hHandle, //hThread:要终止线程的线程句柄
DWORD dwExitCode); //dwExitCode:指定线程返回码,可以调用GetExitCodeThread()查询返回码的含义
函数调用成功,返回一个非0值;若失败,返回0,可调用函数GetLastError()查询失败的原因.
4.挂起线程
Sleep()用于挂起当前正在执行的线程.
VOID Sleep(DWORD dwMilliseconds); //dwMilliseconds:指定挂起时间,单位为ms(毫秒)
5.关闭句柄
函数CloseHandle()用于关闭已打开的对象的句柄,其作用与释放动态申请的内存空间类似,这样可以释放系统资源,使线程安全运行.
BOOL CloseHandle(HANDLE hObject); //hObject:已打开对象的句柄
函数调用成功,返回一个非0值;若失败,返回0,可调用函数GetLastError()查询失败的原因.
6.eatApple()函数:
void eatApple(int apple_number){
Sleep((3 - apple_number) * 1000); //休眠时间 (3 - apple_number)s
printf("I'm eating apple #%d.\n", apple_number);
}
三、实验内容
(一)实验内容
视频部分:
1.认识工具(实验准备部分)
2.比较两个程序
不使用多线程:按照语句出现的位置顺序执行eatApple()函数
使用多线程:同时执行eatApple()函数,根据休眠时长的顺序从小到大输出
讲义部分:
使用系统调用CreatThread()创建一个子线程,并在子线程中显示;Thread is Running!.为了能让用户清楚地看到线程的运行情况,使用Sleep()使线程挂起5s,之后使用ExitThread(0)撤销进程.
(二)主要代码
视频部分:
// 01.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "01.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// The one and only application object
CWinApp theApp;
using namespace std;
void eatApple(int apple_number){
Sleep((3 - apple_number) * 1000);
printf("I'm eating apple #%d.\n", apple_number);
while(1) {
printf("#%d id is exiting.\n\n",apple_number);
ExitThread(0);
}
}
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
HANDLE handle1 = NULL;
HANDLE handle2 = NULL;
HANDLE handle3 = NULL;
DWORD ThreadID1 = NULL;
DWORD ThreadID2 = NULL;
DWORD ThreadID3 = NULL;
int a=0;
int b=1;
int c=2;
handle1=CreateThread((LPSECURITY_ATTRIBUTES) NULL,
0,
(LPTHREAD_START_ROUTINE) eatApple,
(LPVOID)a,
0,
&ThreadID1);
handle2=CreateThread((LPSECURITY_ATTRIBUTES) NULL,
0,
(LPTHREAD_START_ROUTINE) eatApple,
(LPVOID)b,
0,
&ThreadID2);
handle3=CreateThread((LPSECURITY_ATTRIBUTES) NULL,
0,
(LPTHREAD_START_ROUTINE) eatApple,
(LPVOID)c,
0,
&ThreadID3);
Sleep(10000);
return nRetCode;
}
讲义部分:
// ThreadName1.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "ThreadName1.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// The one and only application object
CWinApp theApp;
using namespace std;
void ThreadName()
{
printf("Thread is Running!\n");
}
static HANDLE hHandle1=NULL;
DWORD ThreadID1;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = 0;
HANDLE hHandle = NULL;
DWORD ThreadID = NULL;
hHandle=CreateThread((LPSECURITY_ATTRIBUTES) NULL,
0,
(LPTHREAD_START_ROUTINE) ThreadName,
(LPVOID)NULL,
0,
&ThreadID);
Sleep(5000);
CloseHandle(hHandle);
ExitThread(0);
return nRetCode;
}
四、实验结果与总结
视频部分:
结果:
1.不使用多线程时的输出顺序:0、1、2
2.多线程输出顺序:2、1、0
ExitThread(0)函数在退出线程后即可退出while(1){}死循环。
注意:使用多线程实验时Sleep(10000);语句不能省略,否则会出现主线程结束将子线程也结束的情况。
总结:
1.通过两个程序的执行非常直观的感受使用多线程的区别。
2.多线程同时执行eatApple()函数,并由休眠时长的不同决定输出顺序
3.线程是操作系统中能够进行运算调度的最小单位。
4.多线程使用非常广泛,充分利用网络带宽,提高了程序的效率。
讲义部分:
结果:
总结:
(CreateThread()函数中指明了线程要执行的ThreadName()函数)
遇到的问题与解决方法:
①安装后运行初始程序时发现无法运行,在网上搜索后发现是VC++ 安装在了C盘中的原因。在打开vc++时 右击→以管理员身份运行,即可解决。
②运行程序时报错:关于fatal error C1083: Cannot open precompiled header file: /‘Debug/v13_3.pch/’: 错
百度后得知这个错误是预编译文件的设置造成的,单独编译StdAfx.cpp 即可解决。
③一开始没有发现ThreadName()函数被调用,后来在CreateThread()函数的参数中发现LPSECURITY_START_ROUTINE lpStartAddress 指定了线程要执行的函数,由此,在线程被创建时输出Thread is Running!