📄 15.3 简单多线程示例.txt
字号:
15.3?简单多线程示例
下面编写一个多线程程序。新建一个空的 Win32 Console Application类型的工程,工程取名为:
MultiThreado并为该工程添加一个 C++源文件: MultiThread.cppo然后在其中添加如例 15-1所示
代码。
例 15-1
#include <windows.h>
#include <iostream.h>
DWORD WINAPI Fun1Proc(
LPVOID lpParameter // thread data
void main ()
1. HANDLE hThreadl;
2. hThreadl=CreateThread(NULL, 0, Fun1Proc , NULL, 0, NULL);
3. CloseHandle(hThread1);
4. cout<<"main thread is running"<<endl;
}
//线程 1的入口函数
DWORD WINAPI Fun1Proc (
LPVOID lpParameter // thread data
{
cout<<"thread1 is running"<<endl;
return 0;
上述如例 15-1所示代码创建了一个简单的多线程程序,主要由以下几个部分组成 z
(1)包含必要的头文件因为程序中需要访问 WindowsAPI函数,因此需要包含 windows.h文件,另外
还用到
了 C++的标准输出函数,所以需要包含C++的标准输入输出文件: iostream.h。
(2)线程函数
对于线程入口函数的写法并不需要将其死记硬背住,但是一定要知道如何查到这个函数的写法,等
到使用的次数多了,自然就知道如何编写这个函数了。这里的线程函数 FunlProc的功能非常简单,
就是在标准输出设备上输出一句话:"read1 is running飞然后就退出。
(3) main函数
当程序启动运行后,就会产生主线程, main函数就是主线程的入口函数。在这个主线程中可以创建
新的线程。在上述main函数中首先调用 CreateThread函数创建一个新线程(下面将这个新线程称为
线程1),该函数的第一个参数设置为 NULL,让新线程使用默认的安全性;第二个参数设置为 0,让
新线程采用与调用线程一样的钱大小:第三个参数指定线程1入口函数的地址:第四个参数是传递给
线程1的参数,这里不需要使用这个参数,所,以将其设置为 NULL;第五个参数,即线程创建标记,
设置为 0,让线程一旦创建就立即运行:第六个参数,即新线程的田,因为这里不需要使用该ID,所
以将其设置为NULL。
在创建线程完成之后,调用 CloseHandle函数关闭新线程的句柄。这里,读者可能就会产生这样的
疑问:刚刚创建了线程,为什么又将它关闭了呢?读者应注意,实际上调用 CloseHandle函数并没有
中止新创建的线程,只是表示在主线程中对新创建的线程的引用不感兴趣,因此将它关闭。另一方
面,当关闭该句柄时,系统会递减该线程内核对象的使用计数。当创建的这个新线程执行完毕之后,
系统也会递减该线程内核对象的使用计数。当使用计数为0时,系统就会释放该线程内核对象。如果
没有关闭线程句柄,系统就会一直保持着对线程内核对象的引用,这样,即使该线程执行完毕,它
的引用计数仍不会为0,这样该线程内核对象也就不会被释放,只有等到进程终止时,系统才会清理
这些残留的对象。因此,在程序中,当不再需要线程句柄时,应将其关闭,让这个线程内核对象的
引用计数减1。
接下来, main函数在标准输出设备上输出一句话: "main thread is running"。
Build井运行MultiThread程序。可以看到如图 15.6所示的窗口,在该窗口中输出了一句话: "main
thread is running",表明主线程运行了,但是井没有看到 "threadl is running" 这句话。
图 15.6创建的新线裂并未运行
为什么会出现这样的结果呢?是因为线程创建失败了吗?实际情况并非如此,对于主线程来说,操作
系统为它分配了时间片,因此它能够运行。在上述主线程的入口函数main中,当调用第 2行代码创
建线程后,就会接着执行它的下一行代码,即调用 CloseHandle函数关闭线程句柄,之后就是执行
其第4行代码,在标准输出设备上输出一句话,然后该
函数就退出了,也就是说主线程执行完成了。当主线程执行完毕后,进程也就退出了,这时进程中
所有的资源,包括还没有执行的线程都要退出,也就是说新创建的线程 1还没有机会执行就退出了,
因此在窗口中就没有看到 "threadl is running"这句话。
为了让新创建的线程能够得到执行的机会,就需要使主线程暂停执行,即放弃执行的权利,操作系
统就会从等待运行的线程队列中选择一个线程来执行,这时新创建的线程 1就可以得到执行的机会,
从而输出"出read 1 is running"这句话。
在程序中,如果想让某个线程暂停运行,可以调用 Sleep函数,该函数可使调用线程暂停自己的运
行,直到指定的时间间隔过去为止。该函数的原型声明如下所示 :
void Sleep(DWORD dwMilliseconds);
Sleep函数有一个 DWORD类型的参数,指定线程睡眠的时间值,以毫秒为单位,也就是说,如果将此
参数指定为 1000,实际上是让线程睡眠 1秒钟。
因此,在上述例 15-1所示 main函数的最后添加下面这条语句,让主线程暂停运行 10ms,使其放弃
执行的权利,操作系统就会选择线程 1让其运行。当该线程运行完成之后,或者 10ms间隔时间已过,
主线程就会恢复运行, main函数退出,进程结束。
Sleep(10);
再次运行 MultiThread程序,就会看到如图 15.7所示的窗口。可以看到程序输出了"main thread is
running"和" thread 1 is running"这两句话,说明新创建的线程执行了。
图 15.7简单多线程示例程序成功执行
下面,我们在主线程和线程 l中都进行一个循环,让它们分别不断地输出" main thread is running,,
和Thhread 1 is running"这两句话,看一下这两个线程之间交替执行的情况。为了控制循环的次数,
可以定义一个变量,当其值递增到某个给定值时就停止循环。于是,在上述如例 15-1所示 main函
数之前添加如下语句,定义一个全局的 int类型的变量: index, 以控制循环的次数,并将其初始化
为 0:
int index=0;
然后在上述例 15-1所示 main函数的第 4行代码之前添加下面这条语句,即当 index大于 1000时,
退出 while循环。这时,就可以不需要 Sleep函数了,将其注释起来。
while(index++<1000)
同样,在线程 1的入口函数 FunlProc中,在其第 l行代码前添加上面这条语句。
然后再次运行 Multi Thread程序,将会看到如图 L5.8所示的窗口,在该窗口上,可以看到主线程
和线程 1交替地输出了自己的信息。也就是说,主线程运行一段时间之后,当它的时间片到期后,
操作系统会选择线程 1开始运行,为线程 1分配一个时间片。当线程
1运行一段时间后,它的时间片到期了,操作系统又会选择主线程开始运行。于是就看到主线程和线
程 1在交替运行。这就说明,主线程和其他线程在单CPU平台上是交替运行的,当然,如果在多CPU
平台上,主线程和其创建的线程就可以真正地并发运行了。
图 15.8主线程和其他线程在单CPU平台上交替运行结果
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -