|
||||||||||||||||||
Add Forum to Favorites | Send Topic To a Friend | View Forum FAQ | Track this topic Page: 1 2 »» |
Last Thread Next Thread ![]() |
| The One: A Singleton Discussion |
|
![]() SteveC Member since: 1/9/2000 From: Boston, USA |
||||
|
|
||||
| Regarding Thread Safety In a generic template-based singleton the only assumption you can reasonably make about dealing with thread safety is the creation of the singleton itself. The double-checked locking pattern combined with a mutex/critical section can solve this problem. I agree you can't make assumptions about how to lock the singleton object itself - that is implementation specific. Besides, using a scope-based locking technique inside of GetInstance() would only last for the duration of the call - so it would not protect the singleton once the method call returns. One way maybe you could consider doing it would be to return a kind of smart-pointer/mutex object from the GetInstance() method. That way each call to GetInstance would return a temporary automatic variable which locked the mutex until the call to the real class's method ( accessed via operator-> ) occurred. This is a variation of the prefix/suffix technique described by Stroustrup in an article ( not sure right now if it was C++ Report or C/C++ Users Journal ) |
||||
|
||||
![]() Stoffel Member since: 12/2/1999 From: San Diego, USA |
||||
|
|
||||
quote: Exactly. It's like trying to make an internally-synchronized container; the container has no way of knowing over which calls each thread needs exclusive access. It must be externally synchronized. quote: Possible, but then you force that overhead for single-thread apps, and there's the chance that a user will save the handle, retaining the lock forever. Plus, the lock shouldn't be released on the first method call--how is the singleton object to know over what series of calls a given thread needs exclusive access? Same problem as above. If a user needs a singleton that assures exclusive access over a call period, the singleton can offer a handle object that grabs the mutex on creation, allows access to the object (through ->), and releases it on destruction. Then the user just needs to be sure to only hold the handles on the stack, e.g. release when done. |
||||
|
||||
![]() Void Member since: 9/12/1999 From: Singapore |
||||
|
|
||||
quote: Actually, that would solve the creation problem fine but it becomes a performance problem. [edited by - void on May 25, 2002 9:14:05 AM] |
||||
|
||||
![]() Outworlder Member since: 3/3/2000 From: Fortaleza, Brazil |
||||
|
|
||||
Quoted from the article:quote: I did not understand this part. In the example, the graphics renderer is a member of an Application object. But, isn't this Application object a singleton itself? |
||||
|
||||
![]() Void Member since: 9/12/1999 From: Singapore |
||||
|
|
||||
| The application does not have to be a singleton. At the top upper most level, it can be just an auto variable. ie. If you need to pass renderer to subsystems, let them take a reference. There are certain situations where a singleton would really be useful. For the renderer, IMO, it doesn't qualify. |
||||
|
||||
![]() Kerion Member since: 4/7/2002 From: USA |
||||
|
|
||||
| Maybe I am just missing the point entirely, but whatever ever happened to good old fashion explicit creation/destruction? Its very simple to add an explicit static Create/Destroy function to your singletons which then creates/destroys the static instance of the class. A singleton is simply a class that exists once, there really are no hard set rules on how they are created or destroyed (if you want to get in to the nitty gritty of OO that is). Sure, this requires you to make an extra call our two at application init/de-init, but how many singletons do you plan to have anyway? |
||||
|
||||
![]() Outworlder Member since: 3/3/2000 From: Fortaleza, Brazil |
||||
|
|
||||
quote: Can someone give some examples of these situations? |
||||
|
||||
![]() Void Member since: 9/12/1999 From: Singapore |
||||
|
|
||||
quote: Exception safety. You can't have robust code if you do an explicit creation/destruction. Or at least not letting it turn into a maintanance nightmare down the road. |
||||
|
||||
![]() Void Member since: 9/12/1999 From: Singapore |
||||
|
|
||||
quote: Which situation is not clear? The passing reference? The application holds renderer, sound, input etc. Those classes that require a renderer stores a reference member varibles instead of assuming it was a singleton. These classes should be at high level enough so the application can store them as member variables. |
||||
|
||||
![]() Outworlder Member since: 3/3/2000 From: Fortaleza, Brazil |
||||
|
|
||||
quote: I understand how to use member variables, and how to create and use singletons. I was asking for some examples of situations where a singleton would be preferred to, say, a global or a member variable. Or, even better, advantages and disadvantages of each approach. Until now, my managers, the renderer and a couple of other classes are implemented as a singleton. I can't see any disadvantages of doing so in my program. |
||||
|
||||
![]() Void Member since: 9/12/1999 From: Singapore |
||||
|
|
||||
| Crap, I typed a really long reply and the server lost the message! I'm not gonna type it again I highlighted the problems with singleton here. If you didn't understand, that means I failed Look at cout. You need it to be valid in the whole program. But VC++ 6 version uses meyers singleton and it suffers from destruction order problem. Anyway, check out "An exceptional quest" in the articles section and Modern C++ Design Abstract Factory pattern for why you need singletons. |
||||
|
||||
![]() shurcool Member since: 5/6/2001 From: Toronto, Canada |
||||
|
|
||||
uh... i'm sort of confuzed by the output the nifty counter program gave me:Main INIT MyClass CTOR Method called Main EXIT Method called Press any key to continue and yes, i'm SURE that it's not breakmeyer, but niftycounter! i don't see the MyClass dtor being called. :-\ that's really weird... --- shurcool wwdev |
||||
|
||||
![]() Void Member since: 9/12/1999 From: Singapore |
||||
|
|
||||
| ok, I have replied you in your message too. Thanks for bringing it to my attention. If you are using VC++ 6, the nifty counter output may not show MyClass DTOR as expected. This is because VC++ 6 STL cout implementation uses the meyer's singleton, and it is destroyed before the nifty counter. The solution is to switch STL implementation ( I use www.dinkumware.com VC++ 3.08 STL), use printfs/OutputDebugMessage instead of cout, or use the debugger to set a breakpoint at the myclass dtor to verify that it is actually being called. |
||||
|
||||
![]() Fire-Clankiller Member since: 2/24/2004 |
||||
|
|
||||
quote: The double-checked locking pattern does not work correctly in most (all?) implementations: http://groups.google.de/groups?q=g:thl3178003383d&dq=&hl=de&lr=&ie=UTF-8&selm=MPG.191d9257dfa5ebf79896db%40news.hevanet.com&rnum=1 [edited by - Fire-Clankiller on February 24, 2004 7:59:19 AM] [edited by - Fire-Clankiller on February 24, 2004 8:22:39 AM] |
||||
|
||||
![]() Hiko Member since: 12/5/2004 From: South Africa |
||||
|
|
||||
| would it be a bad idea to make a whole class static, as in all the methods and variables static ? Im thinking of doing this with my renderer library which plugs into the engine for API-independant gfx ? thanks |
||||
|
||||
![]() Eisenbart Member since: 7/27/2006 From: Nurnberg, Germany |
||||
|
|
||||
| Hi everyone, I have just discovered the "Nifty Counter" type of singleton in the article being discussed and I almost thought that this would the solution to a common order of destruction problem. Unfortunately, it relies on the static instance of the nifty counter class to be created before any other static object in a translation unit that might call MyClassNiftyCounter::Get_Instance() in its destructor. Take a look at the following example of a translation unit: struct X
{
~X();
};
X x;
#include "MyClass.h"
X::~X()
{
MyClassNiftyCounter::Get_Instance().Method();
}In the translation unit shown above, the non-local static object of type X will probably be created first. Then, by including "MyClass.h", the static object of type MyClassNiftyCounter will be created and finally be destroyed before the object of type X, thus causing X::~X() to call MyClassNiftyCounter::Get_Instance() when the object of type MyClass has already been destroyed. As order of construction/destruction is undefined across translation units, there is no guarantee that there will be static instance of the nifty counter class in another translation unit that has not been destroyed yet and that can thus prevent the disaster. |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| Thanks for bringing back a very old article. Honestly, you can do everything you need too without a singleton. There are SO few instances a singleton is the RIGHT or best solution. It normally means you're thinking in procedural ways, which is fine for a procedural language like C, but if you're using C++ please stop it :) A singleton, at the end of the day, is just a global. Globals are, for the most part, bad design. I have a complete and working 3D engine for a commercial game and not a single singleton in sight. For Christmas, all I want is articles like these to fade away as better programming practices take hold! Oh and peace on Earth! |
||||
|
||||
![]() PouyaCatnip Member since: 2/11/2004 |
||||
|
|
||||
Quote: ![]() Thats got to be the worst justification for using a singleton I've heard yet. |
||||
|
||||
![]() Eisenbart Member since: 7/27/2006 From: Nurnberg, Germany |
||||
|
|
||||
| The article discusses various types of singletons, but it does not tell you when and how often to use them. I rarely use singletons myself, but if I decide to do so, then why not do it right? That's where the article might be helpful. |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
| I've honestly never come across a time where a singleton was the best solution. Sometimes it's the laziest way (aka best at the time) to go. I used to make my loggers a singleton, then I realized global functions within namespaces is a much, much better solution then to using a singleton. What are your situations when you think a singleton is the best solution? I'm sure the brains here can come up with much better, less procedual and more OOP to boot! |
||||
|
||||
![]() Eisenbart Member since: 7/27/2006 From: Nurnberg, Germany |
||||
|
|
||||
I am using a singleton for a logger, too. It's a memory tracking mechanism that stores information about allocated memory in a database object, which offers methods likeTrackNew( T* ptr, const char* file, int line ); TrackDelete( T* ptr, const char* file, int line ); These methods are called from macros like #define NEW( T ) MemoryTracker::Instance().TrackNew( new T, __FILE__, __LINE__ ) #define DELETE( ptr ) delete MemoryTracker::Instance().TrackDelete( ptr, __FILE__, __LINE__ ) How would you implement that with global functions within namespaces? And isn't that pretty much the same, because a class also acts somewhat (or in this case even exactly) like a namespace? |
||||
|
||||
![]() Void Member since: 9/12/1999 From: Singapore |
||||
|
|
||||
Quote: Ok, this is violating how the nifty counter is meant to be used. The idea behind a nifty counter singleton is the header generates static local variable in global scope (if I remember correctly, the term is glocals) which keeps a reference count. This means nifty counter must not depend on other singleton classes and should be included before other glocals. What you are doing is valid C++, but it violates "good" programming habits, you should import headers before define local definitions. |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
Quote: The global functions fire off a message to the event handler asking for a pointer to the Memory Manager class. They then use that and log the information into that the database. No singleton required. More code? Yes. Better design then a singleton? Hell yes. Someone might argue that you cannot remove your memory manager and place it into another application easily. Mine also has the ability to use a local copy of the manager if my engine library isn't detected. It can also save/read from a file all the data also. It logs pointers etc to the file. Bit more work but its a very robust memory manager. |
||||
|
||||
![]() Eisenbart Member since: 7/27/2006 From: Nurnberg, Germany |
||||
|
|
||||
Quote: I don't quite see the difference towards my implementation. I am retrieving a pointer (well, actually a reference) to the Memory Manager, too. What does the use of an event handler have to do with the use of a singleton? And what's the lifetime of the Memory Manager object whose pointer you are retrieving? Quote: Does your Memory Manager also have the ability NOT to log its data into file, but to keep everything in memory and only write the result (e.g. remaining allocations not having been deleted) into a file? The problem is that logging each allocation or deallocation into a file immediately can significantly slow down an application, it can make it almost entirely freeze up for a while. I know that because my first implementations of a Memory Manager did log everything into a file right away. If you keep all the data about allocations and deallocations in memory until the application terminates (as does my implementation of a Memory Manager), you cannot use a local copy of it. This is why I am using a singleton. |
||||
|
||||
|
Page: 1 2 »» All times are ET (US) ![]() |
Last Thread Next Thread ![]() |
|