windows编程之内核模式下的线程同步

上篇讲到了应用层下的线程同步,接下来看下内核模式下的几种同步方式。

第一种方式称为互斥量。互斥量呢是一个内核对象,它可以确保一个线程对一个资源的独占访问。互斥量和关键段(上篇提到的)的行为是一样的,区别在于一个是内核对象,一个是应用层的对象。因此,互斥量要比关键段慢很多,但是互斥量可以进行跨进程的同步,而关键段并不行。

那么,如何创建一个互斥量呢,MSDN提供了一个名叫做CreateMutex的API,用于创建一个互斥量。其声明如下:

HANDLE CreateMutex(  
  LPSECURITY_ATTRIBUTES lpMutexAttributes,  // 安全属性,一般为NULL
  BOOL bInitialOwner,                       // 创建后是否拥有该互斥量
  LPCTSTR lpName                            // 对象名
);
接下来看一个非常重要的函数,名为WaitForSingleObject,用于等待一个对象是否有信号,其声明如下:

DWORD WaitForSingleObject(  
  HANDLE hHandle,        // 要等待的对象
  DWORD dwMilliseconds   // 等待多长时间
);
在windows中,内核对象有两种状态,一种是有信号的,一种是无信号的。当我们在程序中调用WaitForSingleObject时,若等待的对象是无信号的,那么程序就会阻塞在这里,直到该对象变成有信号或者等待时间到了,若我们在第二个参数传了一个INFINITE(-1)参数时,表示无限等待,若等待的对象一直是无信号的,那么程序将一直阻塞在这里,幸运的是,WaitForSingleObject并不会浪费CPU的时间片。

互斥量其实用的并不是很多,因为它可以进行跨进程的进行同步,那么我们可以用它来进行防止程序的多开,以下是最简单的防多开的代码:

#include <stdio.h>
#include <windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
	//创建一个互斥量	
	HANDLE hMutex = CreateMutex(NULL, TRUE, L"testName");
	//若该互斥量已经存在了,则直接退出
	if (ERROR_ALREADY_EXISTS == GetLastError())
	{
		printf("only one instance can run this mode\n");
		return -1;
	}
	printf("dong something.......");
	system("pause");
	return 0;
}

第二种进行同步的方式叫做事件内核对象。

事件内核对象呢,它有两种类型,第一种是自动重置事件,第二种是手动重置事件.。两者之间的区别在于,当手动重置有信号的时候呢,等待该事件的所有线程都变成可调度的状态,而当自动重置的时候,只有所有等待线程中的一个才能变成可调度的状态。

创建事件,需要调用CreateEvent,其声明如下:

HANDLE CreateEvent(  
  LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性,一般为NULL
  BOOL bManualReset,                       // 是否手动重置
  BOOL bInitialState,                      // 初始的状态,有信号或无信号
  LPCTSTR lpName                           // 对象名
);
如果是手动重置的事件,我们应该将调用ResetEvent和SetEvent来将事件变为无信号和有信号,它们的声明如下:

BOOL ResetEvent(  
  HANDLE hEvent   // 事件对象
);

BOOL SetEvent(  
  HANDLE hEvent   // 事件对象
);
事件通常的用法,就是一个线程要执行,必须先让另一个线程执行完,以下是一个简单的测试代码:

#include <stdio.h>
#include <windows.h>

DWORD WINAPI TestThread(LPVOID lpParam)
{
	//等待事件对象变成有信号状态
	WaitForSingleObject(lpParam, INFINITE);
	printf("sub thread do something...\n");
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	//创建一个事件对象
	HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, L"testName");
	//检查多开
	if (ERROR_ALREADY_EXISTS == GetLastError())
	{
		printf("only one instance can run this mode\n");
		return -1;
	}
	//创建一个线程
	HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)TestThread, hEvent, NULL, NULL);
	
	printf("main thread do something...\n");
	//休息一秒,手动重置事件对象变成有信号,让TestThread运行
	Sleep(1000);
	SetEvent(hEvent);

	system("pause");
	return 0;
}
第三种同步的方式叫做信号量,信号量和其它的内核对象有点不同,我们先来看下创建信号量的API,CreateSemaphore,声明如下:

HANDLE CreateSemaphore(  
  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // 安全属性,一般为NULL
  LONG lInitialCount,                          // 初始的个数
  LONG lMaximumCount,                          // 最大个数
  LPCTSTR lpName                               // 对象名
);
如果当前的计数大于0,信号量将处于有信号的状态,若等于0,信号量处于无信号的状态。

我们可以调用ReleaseSemaphore来递增信号量当前的计数,其声明如下:

BOOL ReleaseSemaphore(  
  HANDLE hSemaphore,       // 信号量对象
  LONG lReleaseCount,      // 增加的个数
  LPLONG lpPreviousCount   // 原先的计数,用不到给NULL
);
以下是信号量的简单测试代码:

#include <stdio.h>
#include <windows.h>

DWORD WINAPI TestThread(LPVOID lpParam)
{
	//等待事件对象变成有信号状态
	WaitForSingleObject(lpParam, INFINITE);
	printf("sub thread do something...\n");
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	//创建一个信号量,初始个数是1个
	HANDLE hSema = CreateSemaphore(NULL, 1, 10, L"testName");
	if (ERROR_ALREADY_EXISTS == GetLastError())
	{
		printf("only one instance can run this mode\n");
		return -1;
	}
	//由于当前个数1大于0,所以是有信号的,直接运行下面代码,且当前个数变为0,即无信号状态
	WaitForSingleObject(hSema, INFINITE);
	printf("main thread do something...\n");
	//创建一个线程
	HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)TestThread, hSema, NULL, NULL);
	//休息1秒后,当前个数变为1,有信号,子线程运行
	Sleep(1000);
	ReleaseSemaphore(hSema, 1, NULL);
	
	system("pause");
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值