一、用户模式和内核模式
Windows操作系统在运行时,存在以下两种模式:
用户模式:是运行应用程序的基本模式,不能访问硬件,且访问的内存的范围也有限制。
内核模式:是操作系统运行的基本模式,可以访问硬件,访问的内存范围无限制。
Windows的程序在运行时,往往频繁的在用户模式和内核模式之间切换。以多线程程序为例,我们所创建的线程属于操作系统,在创建线程中就无法避免从用户模式向内核模式切换,而创建完成后,又会从内核模式切换回应用模式。Windows采用这种双模式的运行方式主要是出于安全的考虑。在用户模式中,应用程序无法访问系统内核的内存,当应用程序出现错误时也就无法导致操作系统异常,最多也就是应用程序自己会发生异常。
在Windows多线程编程中,线程同步技术按照模式也可分为两种:一是用户模式的线程同步技术CRITICAL_SECTION,二是内核模式的线程同步技术事件、互斥和信号量。下面我们分别就这两种模式下不同的线程同步技术进行讨论。
二、试验代码
首先我们引入一段使用多线程的代码:
#include "stdafx.h"
#include "windows.h"
#include "iostream"
int g_i=0;
unsigned WINAPI AddThreadFunc(void* param)
{
for (int i=0;i<1000000;i++)
{
g_i++;
}
return 0;
}
unsigned WINAPI DevThreadFunc(void* param)
{
for (int i=0;i<1000000;i++)
{
g_i--;
}
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE handles[2];
handles[0] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)AddThreadFunc,NULL,0,NULL);
handles[1] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)DevThreadFunc,NULL,0,NULL);
WaitForMultipleObjects(2,handles,true,INFINITE);
std::cout<<"g_i="<<g_i<<std::endl;
return 0;
}
代码的作用是,创建两个线程分别对全局变量g_i进行++和—操作,如果不存在同步问题最后m_i的值将会仍然是0。但是由于我们未进行同步控制,它的结果不可能是0,而且每次运行结果应该不同。
第一次:
第二次:
第三次:
在接下来的讨论中,我们将利用上述四种线程同步技术对改段代码进行同步。
三、用户级别的线程同步技术——CRITICAL_SECTION
CRITICAL_SECTION是用户模式级别的临界区对象。和其他同步技术进行线程同步的原理作用一样,它将临界区的代码上锁,等到执行完临界区代码后再进行解锁。使用它涉及四个windows的API:
void WINAPI InitializeCriticalSection(
__out LPCRITICAL_SECTION lpCriticalSection
);
用来初始化CRITICAL_SECTION对象,接收的参数为CRITICAL_SECTION对象的指针。
void WINAPI DeleteCriticalSection(
__inout LPCRITICAL_SECTION lpCriticalSection
);
用来销毁CRITICAL_SECTION对象,接收的参数为CRITICAL_SECTION对象的指针。
void WINAPI EnterCriticalSection(
__inout LPCRITICAL_SECTION lpCriticalSection
);
对临界区的代码加锁,接收的参数为CRITICAL_SECTION对象的指针。
void WINAPI LeaveCriticalSection(
__inout LPCRITICAL_SECTION lpCriticalSection
);
对临界区的代码解锁,接收的参数为CRITICAL_SECTION对象的指针。
我们在使用临界区对象时,要将它声明为全局对象,使用CRITICAL_SECTION加锁后的代码如下:
#include "stdafx.h"
#include "windows.h"
#include "iostream"
int g_i=0;
CRITICAL_SECTION sec;
unsigned WINAPI AddThreadFunc(void* param)
{
EnterCriticalSection(&sec);
for (int i=0;i<1000000;i++)
{
g_i++;
}
LeaveCriticalSection(&sec);
return 0;
}
unsigned WINAPI DevThreadFunc(void* param)
{
EnterCriticalSection(&sec);
for (int i=0;i<1000000;i++)
{
g_i--;
}
LeaveCriticalSection(&sec);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE handles[2];
InitializeCriticalSection(&sec);
handles[0] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)AddThreadFunc,NULL,0,NULL);
handles[1] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)DevThreadFunc,NULL,0,NULL);
WaitForMultipleObjects(2,handles,true,INFINITE);
std::cout<<"g_i="<<g_i<<std::endl;
DeleteCriticalSection(&sec);
return 0;
}
运行之后,我们得到了正确的结果:
Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
git clone Git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL47