Preamble
This is nothing earth-shattering, just a little trick I've picked up, and like because it is rather slick.
Update
Don't use this code unless you have to. As pointed out in the comments, STL already has this functionality. See
std::mutex[
^] and
std::lock_guard[
^].
Introduction
In C# we use
lock
, without a second thought, to prevent simultaneous access from multiple threads. However in plan vanilla C++, you need to explicitly
EnterCriticalSection
and
LeaveCriticalSection
. Sometimes you may enter, but forget to leave. This tip takes care of that, and makes the code more compact.
Usage
The code is used as shown below:
static Section mySection;
void SomeFunction()
{
{
Lock lock(mySection);
}
}
Simple, isn't it? Basically what you do is, define a
Section
instance for each block of code that should be locked. This is usually a class member, but can easily be global too. Then wrap the portion of code to be locked in a block, and put a
Lock
instance at the top of it, passing the
Section
instance to it.
If you want several blocks that need to be locked from multiple threads, but not from each other, create the same number of
Section
s and use them in each block.
Code
These are the contents of
lock.h, which contains the classes
Lock
and
Section
.
#pragma once
#include <windows.h>
class Lock;
class Section
{
CRITICAL_SECTION criticalSection;
public:
Section()
{
InitializeCriticalSection(&criticalSection);
}
~Section()
{
DeleteCriticalSection(&criticalSection);
}
private:
friend Lock;
void Enter() { EnterCriticalSection(&criticalSection); }
void Leave() { LeaveCriticalSection(&criticalSection); }
};
class Lock
{
Section & section;
public:
Lock(Section & s) : section(s) { section.Enter(); }
~Lock() { section.Leave(); }
};
That's it, quite simple (but see the disclaimer).
How it works
So what happens here? You veterans will find it quite obvious, but I'll explain it as easily as possible for others who don't.
First,
Section
just hides a regular
CRITICAL_SECTION
. Why hide it? Just to prevent unwary maintainers of our code from messing with it. The constructor and destructor initialize and delete the
CRITICAL_SECTION
respectively.
The
Lock
class does the actual locking. When we create an auto variable of this type in the code block, the constructor is called and enters the critical section defined in the
Section
passed to it. Leaving the scope of the block calls the destructor, which in turn calls
LeaveCriticalSection
.
Note that the
Section
field in
Lock
is a reference - this is to prevent the former's constructor from being called again, causing a double initialization of the same
CRITICAL_SECTION
.
See Also
MSDN page for related APIs -
Critical Section Objects.
Disclaimer
This is just the bare-bones code to illustrate the principle, with no exception handling. You'll have to add that as needed. Sample compiled cleanly on Visual C++ 2010 Express, other versions may give warnings.