Solved

C++ using a critical section

Posted on 2014-01-14
10
851 Views
Last Modified: 2014-01-15
I see a lovely Critical Section wrapper at http://scriptionary.com/2008/08/22/criticalsection-wrapper-class/

Now I just need some example code of how to use it properly. This will be for unmanaged C++ code.

Say I have a class, MyClass, and that class has a private variable, myVariable (an integer, or a long if that's better, or unsigned integer, whatever works best, if it makes a difference), and I want to be able to safely write and read that variable from multiple threads, so I need to make sure one thread isn't writing while another is reading.

So I have a class CriticalSection.h defined (from the link above).

And now I have MyClass.h:
#pragma once
class MyClass
{
public:
  MyClass(void);
  ~MyClass(void);

private:
  int myVariable;
  void ThreadSafeWrite(int i);
  int  ThreadSafeRead();

public:
  SetMyVariable(int i) {
    ThreadSafeWrite(i);
  }
  int GetMyVariable(void) {
    return ThreadSafeRead();
  }
};

Open in new window

Now I'm not sure, do I have MyClass inherit CriticalSection? And how would I implement ThreadSafeRead and ThreadSafeWrite?
0
Comment
Question by:deleyd
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 4
10 Comments
 
LVL 25

Accepted Solution

by:
chaau earned 500 total points
ID: 39781110
No, you do not need to use CriticalSection class as a base class for your class. Just create the instance variable of CriticalSection inside your class and use its wrapper Enter() and Leave() methods, like this:
#pragma once
#include "CriticalSection.h" // the header where you will copy/paste the CriticalSection class definition
class MyClass
{
public:
  MyClass(void);
  ~MyClass(void);

private:
  CriticalSection myCS;
  int myVariable;
  void ThreadSafeWrite(int i) 
  {
    myCS.Enter();
    myVariable = i;
    myCS.Leave();
  }
  int  ThreadSafeRead()
  {
    int i;
    myCS.Enter();
    i = myVariable;
    myCS.Leave();
    return i;
  }


public:
  SetMyVariable(int i) {
    ThreadSafeWrite(i);
  }
  int GetMyVariable(void) {
    return ThreadSafeRead();
  }
};

Open in new window

BTW, you can enhance CriticalSection.h by adding a Locker class to it. The locker class will enable the Enter() and Leave() to be done automatically, like this:
class Locker
{
public:
   Locker(CriticalSection *cs):myCS(cs){cs->Enter();}
   ~Locker(){myCS->Leave();}
private:
   CriticalSection *myCS;
}

Open in new window

With this helper class, all you need to do is to have an instance of it in your ThreadSafe functions:
  void ThreadSafeWrite(int i) 
  {
    Locker(&myCS);
    myVariable = i;
  }
  int  ThreadSafeRead()
  {
    Locker(&myCS);
    return myVariable;
  }

Open in new window

0
 
LVL 25

Expert Comment

by:chaau
ID: 39781112
BTW, the usage of Locker class is a good demonstration of C++ RAII
0
 

Author Comment

by:deleyd
ID: 39781192
The Enter/Leave code works. I get error when I replace it with the Locker code.
error C2530: 'myCS' : references must be initialized
#pragma once
#include "CriticalSection.h" // the header where you will copy/paste the CriticalSection class definition
class MyClass
{
public:
  MyClass(void);
  ~MyClass(void);

private:
  CriticalSection myCS;
  int myVariable;
  void ThreadSafeWrite(int i) 
  {
    //myCS.Enter();
    //myVariable = i;
    //myCS.Leave();
    Locker(&myCS);
    myVariable = i;
  }
  int  ThreadSafeRead()
  {
    Locker(&myCS);
    return myVariable;
  }


public:
  void SetMyVariable(int i) {
    ThreadSafeWrite(i);
  }
  int GetMyVariable(void) {
    return ThreadSafeRead();
  }
};

Open in new window

CriticalSection.h (I threw class Locker at the bottom):
#pragma once

#ifdef _WIN32
#   include <windows.h>
#else
#   include <unistd.h>
#   include <pthread.h>
#endif


/**
 * @class A wrapper-class around Critical Section functionality, WIN32 & PTHREADS.
 */
class CriticalSection
{
public:
    /**
     * @brief CriticalSection class constructor.
     */
    explicit CriticalSection(void)
    {
    #ifdef _WIN32
        if (0 == InitializeCriticalSectionAndSpinCount(&m_cSection, 0))
            throw("Could not create a CriticalSection");
    #else
        if (pthread_mutex_init(&m_cSection, NULL) != 0)
            throw("Could not create a CriticalSection");
    #endif
    }; // CriticalSection()
 
    /**
     * @brief CriticalSection class destructor
     */
    ~CriticalSection(void)
    {
        Enter(); // acquire ownership (for pthread)
    #ifdef _WIN32
        DeleteCriticalSection(&m_cSection);
    #else
        pthread_mutex_destroy(&m_cSection);
    #endif
    }; // ~CriticalSection()
 
    /**
     * @fn void Enter(void)
     * @brief Wait for unlock and enter the CriticalSection object.
     * @see TryEnter()
     * @return void
     */
    void Enter(void)
    {
    #ifdef _WIN32
        EnterCriticalSection(&m_cSection);
    #else
        pthread_mutex_lock(&m_cSection);
    #endif
    }; // Enter()
 
    /**
     * @fn void Leave(void)
     * @brief Leaves the critical section object.
     * This function will only work if the current thread
     * holds the current lock on the CriticalSection object
     * called by Enter()
     * @see Enter()
     * @return void
     */
    void Leave(void)
    {
    #ifdef _WIN32
        LeaveCriticalSection(&m_cSection);
    #else
        pthread_mutex_unlock(&m_cSection);
    #endif
    }; // Leave()
 
    /**
     * @fn bool TryEnter(void)
     * @brief Attempt to enter the CriticalSection object
     * @return bool(true) on success, bool(false) if otherwise
     */
    bool TryEnter(void)
    {
        // Attempt to acquire ownership:
    #ifdef _WIN32
        return(TRUE == TryEnterCriticalSection(&m_cSection));
    #else
        return(0 == pthread_mutex_trylock(&m_cSection));
    #endif
    }; // TryEnter()
 
private:
#ifdef _WIN32
    CRITICAL_SECTION m_cSection; //!< internal system critical section object (windows)
#else
    pthread_mutex_t m_cSection; //!< internal system critical section object (*nix)
#endif
}; // class CriticalSection


class Locker
{
public:
   Locker(CriticalSection *cs):myCS(cs){cs->Enter();}
   ~Locker(){myCS->Leave();}
private:
   CriticalSection *myCS;
};

Open in new window

0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 25

Expert Comment

by:chaau
ID: 39781275
I think the error comes from the Locker's destructor. Replace the Locker's code to this:
class Locker
{
public:
   Locker(CriticalSection *cs){myCS = cs; myCS->Enter();}
   ~Locker(){myCS->Leave();}
private:
   CriticalSection *myCS;
};

Open in new window

0
 

Author Comment

by:deleyd
ID: 39781339
Tried. It's complaining about lines 17 and 22:
Locker(&myCS);

Open in new window

error C2530: 'myCS' : references must be initialized
0
 
LVL 25

Expert Comment

by:chaau
ID: 39781926
Ok. The error does not make sense. Please modify the code as follows:
class Locker
{
public:
   Locker(CriticalSection *cs){lockerCS = cs; lockerCS->Enter();}
   ~Locker(){lockerCS->Leave();}
private:
   CriticalSection *lockerCS;
}; 

Open in new window

0
 

Author Closing Comment

by:deleyd
ID: 39782617
It's happy if I assign a variable to Locker(&myCS);
  void ThreadSafeWrite(int i) 
  {
    Locker locker = Locker(&myCS);
    myVariable = i;
  }
  int  ThreadSafeRead()
  {
    Locker locker = Locker(&myCS);
    return myVariable;
  }

Open in new window

0
 
LVL 25

Expert Comment

by:chaau
ID: 39783243
I must have been stupid yesterday. You are right. You need to have a local variable. The correct code would be:
 
void ThreadSafeWrite(int i) 
  {
    Locker locker(&myCS);
    myVariable = i;
  }
  int  ThreadSafeRead()
  {
    Locker locker(&myCS);
    return myVariable;
  } 

Open in new window

you do not need to make unnecessary class copies. Sorry for the mess. Thanks for the points though.
0
 

Author Comment

by:deleyd
ID: 39784073
So
Locker locker = Locker(&myCS);

Open in new window

actually makes a copy? I've been wondering what the proper way is to instantiate a class and store it in a variable. I've been defining variable myClass in *.h:
private:
  MyClass myClass;

Open in new window

and then in *.cpp setting myClass
myClass = MyClass(a,b,c);

Open in new window

I suppose there's a better way?
0
 
LVL 25

Expert Comment

by:chaau
ID: 39784115
when you have a class defined like this:
class A:
{
public:
  A(int i, int j)
  {
     I = i; J =j;
  }
private:
  int I, J;
};

Open in new window

The constructor A(int i, int j) is not the only one that exists for your class. The C++ creates implicitly defined default constructor for you. Read here about this.
So, in your code:
A a = A(1,2);

Open in new window

What happens is:
1. This line A(1,2) creates a temp object.
2. This line A a is your actual object a
3. The a = A(1,2) is actually internally calling
operator=(const A&)

Open in new window

, which is also implicitly defined by the compiler.
4. The temp object created in #1 is destroyed.
If you actually put a breakpoint to the constructor and destructor of A, you will notice that it will be fired twice.

As you can see the creation of the temp object and its copy is completely unnecessary. When I created the Locker class I should have created the explicit constructor and hidden the default ones
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
C++ :Change value from  DisableCMD registry 4 79
object oriented programming comparison 5 93
VS2015 Redefinition errors 4 103
C++ statement T∗ begin(Vector<T>& x) 5 43
Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

734 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question