deleyd
asked on
C++ using a critical section
I see a lovely Critical Section wrapper at http://scriptionary.com/20 08/08/22/c riticalsec tion-wrapp er-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:
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();
}
};
Now I'm not sure, do I have MyClass inherit CriticalSection? And how would I implement ThreadSafeRead and ThreadSafeWrite?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
BTW, the usage of Locker class is a good demonstration of C++ RAII
ASKER
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();
}
};
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;
};
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;
};
ASKER
Tried. It's complaining about lines 17 and 22:
Locker(&myCS);
error C2530: 'myCS' : references must be initialized
Ok. The error does not make sense. Please modify the code as follows:
class Locker
class Locker
{
public:
Locker(CriticalSection *cs){lockerCS = cs; lockerCS->Enter();}
~Locker(){lockerCS->Leave();}
private:
CriticalSection *lockerCS;
};
ASKER
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;
}
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;
}
you do not need to make unnecessary class copies. Sorry for the mess. Thanks for the points though.
ASKER
So
Locker locker = Locker(&myCS);
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;
and then in *.cpp setting myClass
myClass = MyClass(a,b,c);
I suppose there's a better way?
when you have a class defined like this:
So, in your code:
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
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
class A:
{
public:
A(int i, int j)
{
I = i; J =j;
}
private:
int I, J;
};
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);
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&)
, 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