destroying a singleton object

2019-03-30 10:33发布

问题:

What is the best way to destroy a singleton object?

case A: Single threaded Environment
case B: Multi Threaded Environment

Sample snippets(if any) will be really helpful.

[EDIT] I don't have a specific use case I am just trying to understand that IF AT ALL the singleton has to be used how to destroy it correctly. As i understand, from the comments, there are 2 scenarios possible:
1. Destroy the singleton when no code is accessing it.(use smart pointers which will take care of destroying the object by itself using RAII)
2. Destroy a singleton when exiting the program irrespective of whether or not some code was holding on to the singleton. (explicitly destroy by deleting the instance just before main exits)

回答1:

Don't create it in the first place!

Seriously, I strongly recommend you reconsider your choice of singleton, especially in a multithreaded environment. Instead just create an instance in main() and pass it down the call hierarchy to where it is needed.

You can use something like shared_ptr to ensure that the object stays around until no-one needs it any more.



回答2:

Might be able to use atexit() if you only care about cleaning up on successful shutdowns.



回答3:

The backlash against the last decade's overuse of Singletons seem to be in rude health, but they're not entirely evil or unjustified... programming is about compromises and being practical, and it's hard to generalise (generally ;-P). By all means revisit the design and see if you can usefully get rid of them, but if not - so be it.

Anyway, if you want to understand the trade offs, you can't do better than start by reading Alexandrescu's Modern C++ Design, which devotes a chapter to the alternatives for Singletons. Basically, you're asking a silly question here because we don't know what operational constraints your singleton(s) have... what potential interactions, which resources they may need to use and whether they can be re-opened after being closed etc.. So, spit it out or settle for silly answers ;-P.



回答4:

If you're going to use a global, I prefer something like this:

class GlobalThing{ /* ... */ };

GlobalThing *global_thing = 0;

// ...

int main(){
  GlobalThing gt(/* ... */);
  global_thing = >

  // create threads
  // ...
  // kill threads
}

This gives you:

  1. An easily-identified lifetime for the global object.
  2. Resource cleanup in the typical RAII manner.
  3. The above points mean that it works in a multi-threaded environment without worrying about locks, etc., because no threads will exist before or after gt's lifetime. Well, in some environments, you can exit from main() and other threads will keep running, but that is a terrible way to architecture your program for a variety of reasons.

What you still have to worry about:

  1. Initialization order of globals. But, this is unlike the Static Initialization (and Destruction) Order Fiasco, because this technique gives you the benefit of defining the globals' initialization and destruction order (if you define them all this way).
  2. Something else, I'm sure.


回答5:

Leaving aside the question of if it is a good idea.
Which we should do in a separate question!

class S
{
    private:
        S() {}                // private constructor
        S(S const&);          // Undefined copy constructor
        S& operator(S const&) // Undefined assignment operator

    public:
        static S& getInstance()
        {
            /*
             * It is guaranteed to be built on first use
             * and correctly destroyed at the end of the application 
             */
            // Need guard for multi-threaded systems (but not on gcc)
            MULT_THREAD_GUARD;
            static S theOnlyInstance;
            return theOnlyInstance;
        }
};

The multi-thread initialization of the object is the only real problem. You can handle this two ways.

  • You can either put a GUARD to make sure only one thread can enter the getInstance() method when you do multi-threaded builds (iff you use gcc this is not required as it plants the required code automatically to guarantee the object is only initialized once).
  • The other technique is just to make sure the instance is initialized before any threads are created. To do this just call getInstance() in main. Mind you if you do this you may as well have a global variable as you destroy the main benefit of singletons over global variables (lazy initialization). Not that global variables are much better than singletons.

Example Guard

// Then in your platform agnostic header file
#ifndef MULTI_THREAD
#define MULT_THREAD_GUARD       /* Nothing needed in single threaded code */
#else
#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 3 ) && (__GNUC_MINOR__ > 1)))
#define MULT_THREAD_GUARD        /* Nothing required for GCC 3.2 onwards */
#elif defined(YOUR_COMPILERS_MACRO)
#define MULT_THREAD_GUARD        do { /* Put compiler lock code here */ } while (false)
#else
#error "ADD MULTI Thread GUARD for you compiler here"
#endif


回答6:

In multi-threaded,

void Singleton::Destroy() 
{
  if (instance) { 
      pthread_mutex_lock(&lock);
      if (instance) { 
          delete instance;
          instance = 0x0;
      }
      pthread_mutex_unlock(&lock);
  }
}

In single-threaded:

void Singleton::Destroy() 
{
  if (instance) { 
      delete instance;
      instance = 0x0;
  }
}