Okay then. However, at that stage we have the problem that every call to Get involves a mutex, which is presumably bad for performance. |
If you want to multithread, you need mutexes. There's no way around it. Besides, locking a mutex is only really a hinderance if multiple threads are trying to lock it at the same time (which of course is the whole point of mutexes anyway). If only one thread is doing it, then there's no serious penalty.
But in addition to that, this only makes the
creation of the object thread safe. In order to make
using the object threadsafe, you'd have to put acceses behind another mutex.
One option for this would be to put accesses inside a host class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
// Note this assumes that the same mutex can be locked multiple times from the same thread,
// and that locks are accumulating.
// That is, if you lock a mutex 3 times from the same thread, it won't unlock until it gets unlocked
// 3 times.
//
// If this is not how the mutex class works, you'd have to either wrap it inside another class, or
// you'd have to redesign this class to accomidate
class SingletonAccess
{
private:
friend class Singleton;
Mutex& mutex;
Singleton* obj;
SingletonAccess(Mutex& m) : mutex(m), obj(0)
{
mutex.Lock();
}
~SingletonAccess()
{
mutex.Unlock();
}
void operator = (const SingletonAccess&);
public:
SingletonAccess(const SingletonAccess& r) : mutex(r.mutex), obj(r.obj)
{
mutex.Lock();
}
Singleton* operator -> ()
{
return obj;
}
};
//===========================
class Singleton
{
public:
//...
static SingletonAccess Get()
{
SingletonAccess acc( singletonmutex ); // locks the singleton mutex
static Singleton theobj;
acc.obj = &theobj;
return acc;
}
};
|
The trick here is that you don't get a Singleton* directly, instead you get a SingletonAccess object, which ensures that all accesses to the Singleton are behind a mutex lock, and are therefore threadsafe:
1 2 3 4
|
{
SingletonAccess foo = Singleton::Get(); // locks the singleton mutex
foo->DoSomething(); // thread safe call to a singleton function
} // mutex unlocked here
|
EDIT:
Firstly, you have mentioned critical sections and mutexes in your code. I am not very familiar with this stuff, and to be honest don't know the difference. In short, would SFML's sf::Mutex be suitable for the job? |
sf::Mutex is suitable, yes. Although I don't know if it allows for multiple locks from the same thread. That's something you'd have to look up.
A mutex is a device that prevents multiple threads from running sensitive code at the same time. When a mutex is locked in one thread and another thread tries to lock that same mutex, the other thread will have to wait until the first thread unlocks it before it can proceed.
A critical section is just an RAII implementation of a mutex that makes it easier to use and exception safe. The idea is a critical section locks the mutex in its ctor and unlocks it in its dtor, so you don't have to worry about remembering to unlock it when you're done.