网络编程(47)—— windows平台下的四种线程同步技术(一)


一、用户模式和内核模式


        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


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值