Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

A C#-like lock class for C++

4.46/5 (8 votes)
17 Mar 2014CPOL2 min read 41.7K   95  
A very simple class that allows you to easily lock blocks of code from multi-threaded access.

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:
 // All Lock classes using this will be mutually exclusive
static Section mySection;
 
void SomeFunction()
{
    // This block cannot be accessed simultaneously by multiple threads
    {
        Lock lock(mySection);
 
        //
        // any arbitrary code
        //
    }
    // end of locked block
}
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 Sections and use them in each block.

Code

These are the contents of lock.h, which contains the classes Lock and Section.
// Lock.h
#pragma once 
 
#include <windows.h>
 
class Lock; // Forward declaration

// Class to encapsulate CRITICAL_SECTION 
class Section
{
    // Actual critical section that will do the locking
    CRITICAL_SECTION criticalSection;
 
public:
    // Constructor initializes critical section
    Section()
    {
        InitializeCriticalSection(&criticalSection);
    }
 
    // Destructor deletes critical section
    ~Section()
    {
        DeleteCriticalSection(&criticalSection);
    }
 
private:
    friend Lock; // Allow Lock class to access Enter and Leave methods

    // Methods to enter and leave the critical section defined by the field above
    void Enter() { EnterCriticalSection(&criticalSection); }
    void Leave() { LeaveCriticalSection(&criticalSection); }
};
 
// Class that does the locking
class Lock
{
    Section & section; // Section defined by user will be saved here

public:
    // Constructor saves user defined critical section and enters it
    Lock(Section & s) : section(s) { section.Enter(); }
 
    // Destructor leaves critical section
    ~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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)