互斥对象锁和临界区锁性能比较

    在Win32平台上进行多线程编程,常会用到锁。下边用C++实现了互斥对象(Mutex)锁和临界区(CRITICAL_SECTION)锁,以加深理解和今后方便使用。代码已在VS2005环境下编译测试通过。

Lock.h

#ifndef _Lock_H
#define _Lock_H

#include <windows.h>


//锁接口类
class ILock
{
public:
	virtual ~ILock() {}

	virtual void Lock() const = 0;
	virtual void Unlock() const = 0;
};

//互斥对象锁类
class Mutex : public ILock
{
public:
	Mutex();
	~Mutex();

	virtual void Lock() const;
	virtual void Unlock() const;

private:
	HANDLE m_mutex;
};

//临界区锁类
class CriSection : public ILock
{
public:
	CriSection();
	~CriSection();

	virtual void Lock() const;
	virtual void Unlock() const;

private:
	CRITICAL_SECTION m_critclSection;
};


//锁
class CMyLock
{
public:
	CMyLock(const ILock&);
	~CMyLock();

private:
	const ILock& m_lock;
};


#endif

Lock.cpp

#include "Lock.h"

//---------------------------------------------------------------------------

//创建一个匿名互斥对象
Mutex::Mutex()
{
	m_mutex = ::CreateMutex(NULL, FALSE, NULL);
}

//销毁互斥对象,释放资源
Mutex::~Mutex()
{
	::CloseHandle(m_mutex);
}

//确保拥有互斥对象的线程对被保护资源的独自访问
void Mutex::Lock() const
{
	DWORD d = WaitForSingleObject(m_mutex, INFINITE);
}

//释放当前线程拥有的互斥对象,以使其它线程可以拥有互斥对象,对被保护资源进行访问
void Mutex::Unlock() const
{
	::ReleaseMutex(m_mutex);
}

//---------------------------------------------------------------------------

//初始化临界资源对象
CriSection::CriSection()
{
	::InitializeCriticalSection(&m_critclSection);
}

//释放临界资源对象
CriSection::~CriSection()
{
	::DeleteCriticalSection(&m_critclSection);
}

//进入临界区,加锁
void CriSection::Lock() const
{
	::EnterCriticalSection((LPCRITICAL_SECTION)&m_critclSection);
}	

//离开临界区,解锁
void CriSection::Unlock() const
{
	::LeaveCriticalSection((LPCRITICAL_SECTION)&m_critclSection);
}

//---------------------------------------------------------------------------

//利用C++特性,进行自动加锁
CMyLock::CMyLock(const ILock& m) : m_lock(m)
{
	m_lock.Lock();
}

//利用C++特性,进行自动解锁
CMyLock::~CMyLock()
{
	m_lock.Unlock();
}


    下边是测试代码

// MyLock.cpp : 定义控制台应用程序的入口点。
//

#include <iostream>
#include <process.h>
#include <time.h>
#include "Lock.h"

using namespace std;


#define ENABLE_MUTEX
#define ENABLE_CRITICAL_SECTION


#if defined (ENABLE_MUTEX)

//创建一个互斥对象类型锁
Mutex g_Lock;

#elif defined (ENABLE_CRITICAL_SECTION)

//创建一个临界区类型锁
CriSection g_Lock;

#endif


void LockCompare(int &iNum)
{
	CMyLock lock1(g_Lock);

	iNum++;
}


//线程函数
unsigned int __stdcall StartThread(void *pParam)
{
	char *pMsg = (char *)pParam;
	if (!pMsg)
	{
		return (unsigned int)1;
	}

	CMyLock lock2(g_Lock);

	clock_t tStart,tEnd;

	tStart = clock();

	int iNum = 0;
	for (int i = 0; i < 100000; i++)
	{
		LockCompare(iNum);
	}
	
	tEnd = clock();
#if defined (ENABLE_MUTEX)

	cout<<"The lock type is mutex, time = "<<(tEnd - tStart)<<" ms."<<endl;

#elif defined (ENABLE_CRITICAL_SECTION)

	cout<<"The lock type is critical section, time = "<<(tEnd - tStart)<<" ms."<<endl;

#endif

	return (unsigned int)0;
}

int main(int argc, char* argv[])
{
	HANDLE hThread1, hThread2;
	unsigned int uiThreadId1, uiThreadId2;

	char *pMsg1 = "First print thread.";
	char *pMsg2 = "Second print thread.";

	//创建两个工作线程,分别打印不同的消息
	hThread1 = (HANDLE)_beginthreadex(NULL, 0, &StartThread, (void *)pMsg1, 0, &uiThreadId1);
	hThread2 = (HANDLE)_beginthreadex(NULL, 0, &StartThread, (void *)pMsg2, 0, &uiThreadId2);

	//等待线程结束
	DWORD dwRet = WaitForSingleObject(hThread1,INFINITE);
	if ( dwRet == WAIT_TIMEOUT )
	{
		TerminateThread(hThread1,0);
	}
	dwRet = WaitForSingleObject(hThread2,INFINITE);
	if ( dwRet == WAIT_TIMEOUT )
	{
		TerminateThread(hThread2,0);
	}

	//关闭线程句柄,释放资源
	::CloseHandle(hThread1);
	::CloseHandle(hThread2);

	system("pause");
	return 0;
}


    在线程函数StartThread中,循环100000次,对保护资源“iNum ”反复加锁,解锁。编译,运行5次,将每次打印的线程锁切换耗时时间记录下来。之后,将测试代码中的宏 #define ENABLE_MUTEX 注释掉,禁掉互斥锁,启用临界区锁,重新编译代码,运行5次。下边是分别是互斥锁和临界区锁耗时记录(不同机器上耗时会不同):


互斥锁

线程Id

耗时 / ms

总计

1

141

125

125

125

125

641

2

140

125

140

125

156

686

 

临界区锁

线程Id

耗时 / ms

总计

1

15

16

31

31

31

124

2

31

31

31

16

31

140

 

 

    互斥锁总共耗时:641+686=1327 ms,而临界区锁:124+140=264 ms。显而易见,临界区锁耗时比互斥锁耗时节约了大概5倍的时间。

    总结:1、在同一个进程的多线程同步锁,宜用临界区锁,它比较节约线程上下文切换带来的系统开销。但因临界区工作在用户模式下,所以不能对不同进程中的多线程进行同步。2、因互斥对象锁属于内核对象,所以在进行多线程同步时速度会比较慢,但是可以在不同进程的多个线程之间进行同步。


你可能感兴趣的:(多线程,工作,测试,null,System,Class)