进程、线程同步互斥学习 —— 临界区

关于临界区,MSDN是这样介绍的:

Critical Section Objects
A critical section object provides synchronization similar to that provided by a mutex object, except that a critical section can be used only by the threads of a single process. Event, mutex, and semaphore objects can also be used in a single-process application, but critical section objects provide a slightly faster,more efficient mechanism for mutual-exclusion synchronization (a processor-specific test and set instruction). Like a mutex object, a critical section object can be owned by only one thread at a time, which makes it useful for protecting a shared resource from simultaneous access. Unlike a mutex object, there is no way to tell whether a critical section has been abandoned.

临界区是保证某个时刻只有一个线程访问数据的最简单,高效的方法,临界区只能被一个进程拥有。


初始化

The process is responsible for allocating the memory used by a critical section. Typically, this is done by simply declaring a variable of typeCRITICAL_SECTION. Before the threads of the process can use it, initialize the critical section by using theInitializeCriticalSection orInitializeCriticalSectionAndSpinCount function.

void WINAPI InitializeCriticalSection(
  __out  LPCRITICAL_SECTION lpCriticalSection
);
BOOL WINAPI InitializeCriticalSectionAndSpinCount(
  __inout  LPCRITICAL_SECTION lpCriticalSection,
  __in     DWORD dwSpinCount
);
即:初始化CRITICAL_SECTION的function,InitializeCriticalSection和InitializeCriticalSectionAndSpinCount

第一个参数都为一个指向要初始化的CS的指针,dwSpinCount是旋转数。

关于SpinCount(旋转数):

On multiprocessor systems, if the critical section is unavailable, the calling thread spindwSpinCount times before performing a wait operation on a semaphore associated with the critical section. If the critical section becomes free during the spin operation, the calling thread avoids the wait operation. 

Spinning means that when a thread tries to acquire a critical section that is locked, the thread enters a loop, checks to see if the lock is released, and if the lock is not released, the thread goes to sleep. On single-processor systems, the spin count is ignored and the critical section spin count is set to 0 (zero). On multiprocessor systems, if the critical section is unavailable, the calling thread spinsdwSpinCount times before performing a wait operation on a semaphore that is associated with the critical section. If the critical section becomes free during the spin operation, the calling thread avoids the wait operation.
即:当线程试图进入另一线程的临界区时,线程不会立即进入等待,而是进行dwSpinCount次尝试进入临界区失败后,才进入等待状态。使用SpinCount次数循环判断临界区是否可以进入,减少了上下文切换(context switch)所消耗的CPU时间,增加了SpinCount的循环开销。


进入临界区

A thread uses the EnterCriticalSection orTryEnterCriticalSection function to request ownership of a critical section. It uses theLeaveCriticalSection function to release ownership of a critical section. If the critical section object is currently owned by another thread,EnterCriticalSection waits indefinitely for ownership. In contrast, when a mutex object is used for mutual exclusion, the wait functions accept a specified time-out interval. TheTryEnterCriticalSection function attempts to enter a critical section without blocking the calling thread.

When a thread owns a critical section, it can make additional calls toEnterCriticalSection orTryEnterCriticalSection without blocking its execution. This prevents a thread from deadlocking itself while waiting for a critical section that it already owns. 

void WINAPI EnterCriticalSection(
  __inout  LPCRITICAL_SECTION lpCriticalSection
);
BOOL WINAPI TryEnterCriticalSection(
  __inout  LPCRITICAL_SECTION lpCriticalSection
);
EnterCriticalSection与TryEnterCriticalSection区别:
The difference is that TryEnterCriticalSection returns immediately, regardless of whether it obtained ownership of the critical section, whileEnterCriticalSection blocks until the thread can take ownership of the critical section.

即:TryEnterCriticalSection以非阻塞尝试进入临界区,EnterCriticalSection相反。


退出临界区

When it has finished executing the protected code, the thread uses the LeaveCriticalSection function to relinquish ownership, enabling another thread to become the owner and gain access to the protected resource. The thread must call LeaveCriticalSection once for each time that it entered the critical section.

void WINAPI LeaveCriticalSection(
  __inout  LPCRITICAL_SECTION lpCriticalSection
);
当需要保护的代码结束后,离开临界区。

注:If a thread calls LeaveCriticalSection when it does not have ownership of the specified critical section object, an error occurs that may cause another thread usingEnterCriticalSection towait indefinitely. 


删除临界区

Deleting a critical section object releases all system resources used by the object.

After a critical section object has been deleted, do not reference the object in any function that operates on critical sections (such asEnterCriticalSection,TryEnterCriticalSection, andLeaveCriticalSection) other thanInitializeCriticalSection andInitializeCriticalSectionAndSpinCount. If you attempt to do so, memory corruption and other unexpected errors can occur.

If a critical section is deleted while it is still owned, the state of the threads waiting for ownership of the deleted critical section is undefined.

void WINAPI DeleteCriticalSection(
  __inout  LPCRITICAL_SECTION lpCriticalSection
);

实例:

Lock.h

#pragma once
#include <windows.h>

//接口
class ILock
{
public:
	virtual void lock()  = 0;
	virtual void unlock() = 0;
};

//临界区封装
class CriticalSection : public ILock
{
public:
	CriticalSection(DWORD dwSpinCount = 0);
	~CriticalSection();
	DWORD setCSspincount(DWORD dwSpinCount = 0);

	virtual void lock();
	virtual void unlock();
private:
	CRITICAL_SECTION m_cs;
};

//锁
class CLock
{
public:
	CLock(ILock&);
	~CLock();
private:
	ILock& m_lock;
};
Lock.cpp

#include "stdafx.h"
#include "Lock.h"

CriticalSection::CriticalSection(DWORD dwSpinCount /* = 0 */)
{
	if (dwSpinCount > 0)
	{
		::InitializeCriticalSectionAndSpinCount(&m_cs, dwSpinCount);
	}
	else
	{
		::InitializeCriticalSection(&m_cs);
	}
}

CriticalSection::~CriticalSection()
{
	::DeleteCriticalSection(&m_cs);
}

void CriticalSection::lock() 
{
	::EnterCriticalSection(&m_cs);
}

void CriticalSection::unlock() 
{
	::LeaveCriticalSection(&m_cs);
}

DWORD CriticalSection::setCSspincount(DWORD dwSpinCount /* = 0 */)
{
	return ::SetCriticalSectionSpinCount(&m_cs, dwSpinCount);
}

CLock::CLock(ILock& locker) : m_lock(locker)
{
	m_lock.lock();
}

CLock::~CLock()
{
	m_lock.unlock();
}
test.cpp

#include "stdafx.h"
#include <iostream>
#include <process.h>
#include "Lock.h"
#define THREADCOUNT 10
CriticalSection g_eatCs;
int nFood = 0;

unsigned int WINAPI EatThread(void *pParam)
{
	int i = (int)pParam;
	int nHasEaten = 0;
	while (true)
	{
		Sleep(100);
		//局部对象
		CLock lock(g_eatCs);
		if (nFood > 0)
		{
			std::cout << "消费者" << i << "进行消费,已经吃掉("<< ++nHasEaten <<"),当前剩余食物" << --nFood << std::endl;
		}
		else
		{
			break;
		}
	}
	return 0;
}

unsigned int WINAPI ProductThread(void *pParam)
{
	int i = 0;
	while (i < 50)
	{
		std::cout << "生产者进行生产,当前剩余食物" << ++nFood << std::endl;
		i++;
	}
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	HANDLE hProductThread;
	HANDLE hEatThread[THREADCOUNT];

	hProductThread = (HANDLE)_beginthreadex(NULL, 0, &ProductThread,(void *)0, 0, 0);
//	WaitForSingleObject(hProductThread, INFINITE);

	for (int i = 0; i < THREADCOUNT; i++)
	{
		hEatThread[i] = (HANDLE)_beginthreadex(NULL, 0, &EatThread, (void *)i, 0, 0);
	}
	WaitForMultipleObjects(THREADCOUNT, hEatThread, TRUE, INFINITE);
	
	::CloseHandle(hProductThread);
	for (int i = 0; i < THREADCOUNT; i++)
	{
		::CloseHandle(hEatThread[i]);
	}

	system("pause");
	return 0;
}

测试结果:




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值