[C++] Multithreading and Critical Data (class)

Started by
4 comments, last by SouthernMunk 18 years, 4 months ago
Can two threads simultaneously call a member function of a single class? The reason I ask is because I want only one thread to have access to the class at a time.

// The critical class that is to be accessed via multiple threads.
class MyClass
{
public:
	MyClass()
	{
		m_hMutex = CreateMutex(NULL, FALSE, NULL);
	};
	void Process()
	{
		WaitForSingleObject(m_hMutex, INFINITE);
		// ... process stuff here...
		ReleaseMutex(m_hMutex);
	};
	~MyClass()
	{
		CloseHandle(m_hMutex);
	};
private:
	HANDLE m_hMutex;
};

// Global MyClass object.
MyClass a;

// Thread procedure
DWORD WINAPI ThreadProc(LPVOID *lpParam)
{
	for (int x = 0; x < 10; x++)
	{
		a.Process();
	}

	return 0;
}

// Entry point.
int main()
{
	// Spawn multiple threads
	// Close threads.

	return 0;
}

In the above, all the threads will be calling a.Process(), which itself calls WaitForSingleObject(), so that only the calling thread has access to the object at that time. Will this work, or is an error likely to occur? Thanks.
Woooooooot!
Advertisement
Im not familiar with windows threads... only posix threads.

Anyway, If that method performs some action (read/write) on critical (shared) resources then it should be protected by an monitor (mutex) so only one thread can execute the method body at a time.

This kind of operations (race conditions) should be automated as in Java synchronize, or with a pre-processor like OpenC++ witch performs code transformations on C++ code. Btw, I use OpenC++ to have something like Java synchronize and other cool stuff like RTTI (not typeid).
Thanks Promag. The only critical data is the mutex (a private member variable). When Thread 1 is created, it calls a.Process(), which "locks" the mutex, performs all other operations, then "unlocks" the mutex. When Thread 2 calls a.Process(), it will wait until Thread 1 has finished, as the final line of MyClass::Process "unlocks" the mutex.

At least, that is how it is supposed to work.
Woooooooot!
Yes, multiple threads can call the same method on the same object at the same time. You can use a mutex just like you suggest, but under Windows, a CRITICAL_SECTION is more efficient (by a fair margin) as long as the threads are all in the same address space (which they likely are unless you're really exotic).

In Java, you can declare a method synchronized so that the VM does the work for you, but not so in C++.

Also, you probably want to wrap the work inside a try/catch block, or use a lock-holder class to hold your critical section lock, so that exceptions don't end up forgetting to release the lock.
enum Bool { True, False, FileNotFound };
Quote:Original post by SouthernMunk
Can two threads simultaneously call a member function of a single class? The reason I ask is because I want only one thread to have access to the class at a time.

As hplus0603 said you can call the same method on the same object at the same time but this code will work only for this one global instance of MyClass class. If you will create another instance of MyClass in your application then you are doomed :) because all instances do not share m_hMutex variable.

Quote:Original post by mezonx
As hplus0603 said you can call the same method on the same object at the same time but this code will work only for this one global instance of MyClass class. If you will create another instance of MyClass in your application then you are doomed :) because all instances do not share m_hMutex variable.


Yeah, I'm actually writing a class template for singletons. Here is how it looks so far:

#pragma once#ifndef SINGLETON_H__#define SINGLETON_H__// Includes:#include <windows.h>// Class Implementation.template<typename T>class Singleton{public:	static void  _SingletonInitialise();	static void* _SingletonInstance();	static void  _SingletonRelease(T** ppLastReferer);	static void  _SingletonDelete();protected:	static Singleton<T>* m_pSingletonInstance;	static CRITICAL_SECTION* m_pSingletonLock;};// Set the instance to NULL.template<typename T>Singleton<T>* Singleton<T>::m_pSingletonInstance = NULL;// Set the pointer to the critical section to NULL.template<typename T>CRITICAL_SECTION* Singleton<T>::m_pSingletonLock = NULL;// Singleton Initialiser.template<typename T>void Singleton<T>::_SingletonInitialise(){	// If the critical section has not been created, do so now.	if (m_pSingletonLock == NULL)		m_pSingletonLock = new CRITICAL_SECTION;	// Initialise the critical section.	InitializeCriticalSection(m_pSingletonLock);	// If the instance has not be created, do so now.	if (m_pSingletonInstance == NULL)		m_pSingletonInstance = new T;}// Get the instance.template<typename T>void* Singleton<T>::_SingletonInstance(){	// Enter the critical section.	EnterCriticalSection(m_pSingletonLock);	return (T*)m_pSingletonInstance;}template<typename T>void Singleton<T>::_SingletonRelease(T** ppLastRefer){	// Reset the caller.	if (ppLastReferer != NULL) (*ppLastReferer) = NULL;	// Leave the critical section.	LeaveCriticalSection(m_pSingletonLock);}template<typename T>void Singleton<T>::_SingletonDelete(){	// Enter the critical section.	EnterCriticalSection(m_pSingletonLock);	// If the instance exists, delete it.	if (m_pSingletonInstance != NULL)	{		delete m_pSingletonInstance;		m_pSingletonInstance = NULL;	}	// Leave the critical section.	LeaveCriticalSection(m_pSingletonLock);	// Delete the critical section.	DeleteCriticalSection(m_pSingletonLock);	delete m_pSingletonLock;}#endif // #ifndef SINGLETON_H__


So basically you can create a singleton class like so:

class GCamera : public Singleton<GCamera>{	// Making Singleton<GCamera> a friend class is necessary, so that when	//	the deletion of the single GCamera object occurs, the private	//	destructor can be called.	friend class Singleton<GCamera>;public:	// ... whatever set of functions you want.private:	// Constructors and destructors can be private, so that the friend	//	class, Singleton<GCamera>, can delete GCamera while non-members	//	cannot.	GCamera();	~GCamera();};


When the primary thread begins, it initialises all the singleton classes and spawns the various secondary threads. After the secondary threads have terminated, the primary thread deletes the singleton classes.

void Initialise(){	GCamera::_SingletonInitialise();	// All other singleton objects.}DWORD WINAPI RenderingThreadProc(void *lpParam){	// Get the GCamera instance.	GCamera* pCamera = (GWindow*)GCamera::_SingletonInstance();	// Any processing, etc, here.	pCamera->DoStuff();	// Make sure to call _SingletonRelease() so that other threads can have	//	access to the object.	GCamera::_SingletonRelease(&pCamera);}void Shutdown(){	GCamera::_SingletonDelete();	// All other singleton objects.}int WINAPI WinMain(...){	Initialise();	// Initialise rendering thread, network communication thread, etc.	Shutdown();	return 0;}


Anyway, this is just the general idea of where I want to go. I love programming, but I'm a slow leaner and am still experimenting before properly starting the development of my game.

So does the above look OK? It compiles fine, but I haven't had a chance to run it.

[Edited by - SouthernMunk on December 16, 2005 12:47:17 AM]
Woooooooot!

This topic is closed to new replies.

Advertisement