@(C Plus Plus)
多线程的概念在操作系统里面有详细的介绍,这里就不浪费时间了。具体的介绍一下C++
编程语言里的多线程编程
。
在有些编程语言中,对多线程或者并发的支持是直接内建在语言中,比如Ada
和VHDL
,但是在C++
里面,对多线程的支持是由具体的操作系统提供的函数接口
来支持的,不同的操作系统具体实现方法不同。
前面的一篇文章(Linux下多线程(C语言))介绍了如何在Linux
下进行多线程编程,Linux
环境下的多线程编程是基于pthread
,遵循POSIX
线程接口,需要使用pthread.h
头文件,链接的时候需要使用库libpthread.a
,是用过clone()
实现的,类似于fork()
。
这篇文章介绍一下在Windows
下多线程编程的基本知识。
进程的创建
线程的状态
在一个线程的生存周期内,存在多种县城的状态,但是基于不同的操作系统平台,也有不同的线程模型,定义的线程的状态也有不同,但是总体来说,线程的几种状态还是比较通用的:
- 就绪:参与调度,等待执行,一旦获取CPU使用权,立即执行;
- 运行:使用CPU,正在运行;
- 休眠:暂不参加调度,等待需要的资源满足后,触发事件进入就绪状态;
- 终止:线程运行结束,等待回收线程资源。
线程的资源
线程存在于进程中,进程中所有的资源对于进程内部的线程都是可见的,进程中典型的资源如下:
- 代码区:进程内部的代码对于每个线程都是可见的,代码区只有一份实体;
- 静态存储区:
- 全局变量:
- 静态变量:
- 动态存储区:
线程内部的资源有:
- 本地栈空间:存放本线程的函数调用栈,函数内部变量;
- 部分寄存器:
主线程与从线程
一个进程开始运行后,就会产生一个缺省的线程,通常这个线程为主线程
。C++
程序中主线程就是通过main
函数进入线程,由主线程衍生的线程称作是从线程
,从线程也有自己的函数入口,作用类似于main
函数。对于普通的线程模型,主线程比较特殊,它与其他线程之间是父子线程的关系
,但是从线程之间是对等关系(peer to peer
),不存在隐含的层次关系。
线程的入口函数
线程都有自己的入口函数,pthread
和winapi
都是使用函数指针的方式传递入口函数,同时也可以指定函数的参数,但是函数的参数都是void *
类型,pthread
的返回类型是void *
类型,winapi
的函数返回类型是unsigned int
类型,线程的入口函数是全局函数。
Windows下多线程编程
创建多线程
Windows SDK
提供了创建进程的函数CreateThread()
,该函数的定义如下:
1 | HANDLE CreateThread( |
参数简介:
LPSECURITY_ATTRIBUTES lpsa
是安全树形结构体,主要控制该线程的句柄可否为进程的子进程继承使用,默认使用NULL表示不能继承;若想继承线程句柄,则需要设置改结构体,将结构体的bInheritHandle成员初始化为TRUE;DWORD cbStack
表示线程初始栈的大小,若是0表示采用默认大小初始化;LPTHREAD_START_ROUTINE lpStartAddr
表示线程开始的位置,即函数入口位置;LPVOID lpvThreadParam
用来接收线程过程函数的参数,不需要时可以设置为NULL;DWORD fdwCreate
表示线程创建时的标志,CREATE_SUSPENDED
表示线程创建后挂起暂不执行,必须调用ResumeThread
才可以执行,0表示线程创建后立即执行;LPDWORD lpIDThread
保存线程的ID
下面一个简单的程序来演示Windows下多线程编程:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31#include <iostream>
#include <cstdlib>
#include <windows.h>
using namespace std;
DWORD WINAPI FunProc(LPVOID lpParam);
int main()
{
int j = 0;
HANDLE hThread1 = CreateThread(NULL,
0,
FunProc,
NULL,
0,
NULL);
CloseHandle(hThread1);
while(j++ < 100)
cout<<" Main Thread is running for "<<"the "<<j<<" times "<<endl;
system("pause");
return 0;
}
DWORD WINAPI FunProc(LPVOID lpParam)
{
int i = 0;
while(i++ < 100)
cout<<" Thread 1 is running for "<<"the "<<i<<" times "<<endl;
return 0;
}
互斥信号量
多线程编程避免不了访问临界区的问题,创建互斥量的函数的定义如下:
1 | HANDLE CreateMutex( |
参数简介:
LPSECURITY_ATTRIBUTES lpMutexAttributes
与前面的一样,是安全结构体,默认是NULL;BOOL bInitialOwner
其值为FALSE表示创建Mutex,不指定所有权,其值为TRUE,指定当前创建进程的ID为所有者,其他线程访问需要先ReleaseMutexLPCTSTR lpName
表示用于设置Mutex的名字,为NULL时表示匿名互斥量。
使用互斥量的另外两个函数是:WaitForSingleObject()
:请求一个互斥量的访问权;ReleaseMutex()
:释放一个互斥量的访问权。
一个简单的应用程序:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65#include <iostream>
#include <cstdlib>
#include <windows.h>
using namespace std;
DWORD WINAPI Fun1Proc(LPVOID lpParam);
DWORD WINAPI Fun2Proc(LPVOID lpParam);
int tickets = 100;
HANDLE hMutex;
int main()
{
int j = 0;
hMutex = CreateMutex(NULL,FALSE,NULL);
HANDLE hThread1 = CreateThread(NULL,
0,
Fun1Proc,
NULL,
0,
NULL);
HANDLE hThread2 = CreateThread(NULL,
0,
Fun2Proc,
NULL,
0,
NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
system("pause");
return 0;
}
DWORD WINAPI Fun1Proc(LPVOID lpParam)
{
while(true)
{
WaitForSingleObject(hMutex,INFINITE);
if(tickets > 0)
{
Sleep(10);
cout<<"Thread 1 sell tickets : "<<tickets--<<endl;
}
else
break;
ReleaseMutex(hMutex);
}
return 0;
}
DWORD WINAPI Fun2Proc(LPVOID lpParam)
{
while(true)
{
WaitForSingleObject(hMutex,INFINITE);
if(tickets > 0)
{
Sleep(10);
cout<<"Thread 2 sell tickets : "<<tickets--<<endl;
}
else
break;
ReleaseMutex(hMutex);
}
return 0;
}